Decouple cart/market DBs: denormalize product data, AP internal inbox, OAuth scraper auth
Remove cross-DB relationships (CartItem.product, CartItem.market_place, OrderItem.product) that break with per-service databases. Denormalize product and marketplace fields onto cart_items/order_items at write time. - Add AP internal inbox infrastructure (shared/infrastructure/internal_inbox*) for synchronous inter-service writes via HMAC-authenticated POST - Cart inbox blueprint handles Add/Remove/Update rose:CartItem activities - Market app sends AP activities to cart inbox instead of writing CartItem directly - Cart services use denormalized columns instead of cross-DB hydration/joins - Add marketplaces-by-ids data endpoint to market service - Alembic migration adds denormalized columns to cart_items and order_items - Add OAuth device flow auth to market scraper persist_api (artdag client pattern) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,25 +1,55 @@
|
||||
from types import SimpleNamespace
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from shared.models.market import CartItem
|
||||
from .identity import current_cart_identity
|
||||
|
||||
async def get_cart(session):
|
||||
ident = current_cart_identity()
|
||||
|
||||
filters = [CartItem.deleted_at.is_(None)]
|
||||
if ident["user_id"] is not None:
|
||||
filters.append(CartItem.user_id == ident["user_id"])
|
||||
else:
|
||||
filters.append(CartItem.session_id == ident["session_id"])
|
||||
|
||||
result = await session.execute(
|
||||
select(CartItem)
|
||||
.where(*filters)
|
||||
.order_by(CartItem.created_at.desc())
|
||||
.options(
|
||||
selectinload(CartItem.product),
|
||||
selectinload(CartItem.market_place),
|
||||
)
|
||||
)
|
||||
return result.scalars().all()
|
||||
def _attach_product_namespace(ci: CartItem) -> None:
|
||||
"""Build a SimpleNamespace 'product' from denormalized columns for template compat."""
|
||||
ci.product = SimpleNamespace(
|
||||
id=ci.product_id,
|
||||
title=ci.product_title,
|
||||
slug=ci.product_slug,
|
||||
image=ci.product_image,
|
||||
brand=ci.product_brand,
|
||||
regular_price=ci.product_regular_price,
|
||||
special_price=ci.product_special_price,
|
||||
regular_price_currency=ci.product_price_currency,
|
||||
)
|
||||
|
||||
|
||||
def _attach_market_place_namespace(ci: CartItem) -> None:
|
||||
"""Build a SimpleNamespace 'market_place' from denormalized columns."""
|
||||
if ci.market_place_id:
|
||||
ci.market_place = SimpleNamespace(
|
||||
id=ci.market_place_id,
|
||||
name=ci.market_place_name,
|
||||
container_id=ci.market_place_container_id,
|
||||
)
|
||||
else:
|
||||
ci.market_place = None
|
||||
|
||||
|
||||
async def get_cart(session):
|
||||
ident = current_cart_identity()
|
||||
|
||||
filters = [CartItem.deleted_at.is_(None)]
|
||||
if ident["user_id"] is not None:
|
||||
filters.append(CartItem.user_id == ident["user_id"])
|
||||
else:
|
||||
filters.append(CartItem.session_id == ident["session_id"])
|
||||
|
||||
result = await session.execute(
|
||||
select(CartItem)
|
||||
.where(*filters)
|
||||
.order_by(CartItem.created_at.desc())
|
||||
)
|
||||
items = list(result.scalars().all())
|
||||
|
||||
for ci in items:
|
||||
_attach_product_namespace(ci)
|
||||
_attach_market_place_namespace(ci)
|
||||
|
||||
return items
|
||||
|
||||
Reference in New Issue
Block a user