fix: correct tojson|safe usage in templates and update validator
- Remove |safe from |tojson in HTML attributes (x-data) - quotes must become " for browsers to parse correctly - Update LANG-002 and LANG-003 architecture rules to document correct |tojson usage patterns: - HTML attributes: |tojson (no |safe) - Script blocks: |tojson|safe - Fix validator to warn when |tojson|safe is used in x-data (breaks HTML attribute parsing) - Improve code quality across services, APIs, and tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,20 +30,14 @@ class LetzshopClientError(Exception):
|
||||
class LetzshopAuthError(LetzshopClientError):
|
||||
"""Raised when authentication fails."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class LetzshopAPIError(LetzshopClientError):
|
||||
"""Raised when the API returns an error response."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class LetzshopConnectionError(LetzshopClientError):
|
||||
"""Raised when connection to the API fails."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# GraphQL Queries
|
||||
|
||||
@@ -6,7 +6,7 @@ Handles secure storage and retrieval of per-vendor Letzshop API credentials.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timezone
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -24,14 +24,10 @@ DEFAULT_ENDPOINT = "https://letzshop.lu/graphql"
|
||||
class CredentialsError(Exception):
|
||||
"""Base exception for credentials errors."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CredentialsNotFoundError(CredentialsError):
|
||||
"""Raised when credentials are not found for a vendor."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class LetzshopCredentialsService:
|
||||
"""
|
||||
@@ -54,9 +50,7 @@ class LetzshopCredentialsService:
|
||||
# CRUD Operations
|
||||
# ========================================================================
|
||||
|
||||
def get_credentials(
|
||||
self, vendor_id: int
|
||||
) -> VendorLetzshopCredentials | None:
|
||||
def get_credentials(self, vendor_id: int) -> VendorLetzshopCredentials | None:
|
||||
"""
|
||||
Get Letzshop credentials for a vendor.
|
||||
|
||||
@@ -72,9 +66,7 @@ class LetzshopCredentialsService:
|
||||
.first()
|
||||
)
|
||||
|
||||
def get_credentials_or_raise(
|
||||
self, vendor_id: int
|
||||
) -> VendorLetzshopCredentials:
|
||||
def get_credentials_or_raise(self, vendor_id: int) -> VendorLetzshopCredentials:
|
||||
"""
|
||||
Get Letzshop credentials for a vendor or raise an exception.
|
||||
|
||||
@@ -293,9 +285,7 @@ class LetzshopCredentialsService:
|
||||
# Connection Testing
|
||||
# ========================================================================
|
||||
|
||||
def test_connection(
|
||||
self, vendor_id: int
|
||||
) -> tuple[bool, float | None, str | None]:
|
||||
def test_connection(self, vendor_id: int) -> tuple[bool, float | None, str | None]:
|
||||
"""
|
||||
Test the connection for a vendor's credentials.
|
||||
|
||||
@@ -364,7 +354,7 @@ class LetzshopCredentialsService:
|
||||
if credentials is None:
|
||||
return None
|
||||
|
||||
credentials.last_sync_at = datetime.now(timezone.utc)
|
||||
credentials.last_sync_at = datetime.now(UTC)
|
||||
credentials.last_sync_status = status
|
||||
credentials.last_sync_error = error
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ architecture rules (API-002: endpoints should not contain business logic).
|
||||
"""
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timezone
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import func
|
||||
@@ -21,21 +21,16 @@ from models.database.letzshop import (
|
||||
)
|
||||
from models.database.vendor import Vendor
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class VendorNotFoundError(Exception):
|
||||
"""Raised when a vendor is not found."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class OrderNotFoundError(Exception):
|
||||
"""Raised when a Letzshop order is not found."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class LetzshopOrderService:
|
||||
"""Service for Letzshop order database operations."""
|
||||
@@ -114,17 +109,23 @@ class LetzshopOrderService:
|
||||
or 0
|
||||
)
|
||||
|
||||
vendor_overviews.append({
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_name": vendor.name,
|
||||
"vendor_code": vendor.vendor_code,
|
||||
"is_configured": credentials is not None,
|
||||
"auto_sync_enabled": credentials.auto_sync_enabled if credentials else False,
|
||||
"last_sync_at": credentials.last_sync_at if credentials else None,
|
||||
"last_sync_status": credentials.last_sync_status if credentials else None,
|
||||
"pending_orders": pending_orders,
|
||||
"total_orders": total_orders,
|
||||
})
|
||||
vendor_overviews.append(
|
||||
{
|
||||
"vendor_id": vendor.id,
|
||||
"vendor_name": vendor.name,
|
||||
"vendor_code": vendor.vendor_code,
|
||||
"is_configured": credentials is not None,
|
||||
"auto_sync_enabled": credentials.auto_sync_enabled
|
||||
if credentials
|
||||
else False,
|
||||
"last_sync_at": credentials.last_sync_at if credentials else None,
|
||||
"last_sync_status": credentials.last_sync_status
|
||||
if credentials
|
||||
else None,
|
||||
"pending_orders": pending_orders,
|
||||
"total_orders": total_orders,
|
||||
}
|
||||
)
|
||||
|
||||
return vendor_overviews, total
|
||||
|
||||
@@ -210,9 +211,7 @@ class LetzshopOrderService:
|
||||
letzshop_order_number=order_data.get("number"),
|
||||
letzshop_state=shipment_data.get("state"),
|
||||
customer_email=order_data.get("email"),
|
||||
total_amount=str(
|
||||
order_data.get("totalPrice", {}).get("amount", "")
|
||||
),
|
||||
total_amount=str(order_data.get("totalPrice", {}).get("amount", "")),
|
||||
currency=order_data.get("totalPrice", {}).get("currency", "EUR"),
|
||||
raw_order_data=shipment_data,
|
||||
inventory_units=[
|
||||
@@ -236,13 +235,13 @@ class LetzshopOrderService:
|
||||
|
||||
def mark_order_confirmed(self, order: LetzshopOrder) -> LetzshopOrder:
|
||||
"""Mark an order as confirmed."""
|
||||
order.confirmed_at = datetime.now(timezone.utc)
|
||||
order.confirmed_at = datetime.now(UTC)
|
||||
order.sync_status = "confirmed"
|
||||
return order
|
||||
|
||||
def mark_order_rejected(self, order: LetzshopOrder) -> LetzshopOrder:
|
||||
"""Mark an order as rejected."""
|
||||
order.rejected_at = datetime.now(timezone.utc)
|
||||
order.rejected_at = datetime.now(UTC)
|
||||
order.sync_status = "rejected"
|
||||
return order
|
||||
|
||||
@@ -255,7 +254,7 @@ class LetzshopOrderService:
|
||||
"""Set tracking information for an order."""
|
||||
order.tracking_number = tracking_number
|
||||
order.tracking_carrier = tracking_carrier
|
||||
order.tracking_set_at = datetime.now(timezone.utc)
|
||||
order.tracking_set_at = datetime.now(UTC)
|
||||
order.sync_status = "shipped"
|
||||
return order
|
||||
|
||||
|
||||
Reference in New Issue
Block a user