Decouple all cross-app service calls to HTTP endpoints
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m0s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m0s
Replace every direct cross-app services.* call with HTTP-based communication: call_action() for writes, fetch_data() for reads. Each app now registers only its own domain service. Infrastructure: - shared/infrastructure/actions.py — POST client for /internal/actions/ - shared/infrastructure/data_client.py — GET client for /internal/data/ - shared/contracts/dtos.py — dto_to_dict/dto_from_dict serialization Action endpoints (writes): - events: 8 handlers (ticket adjust, claim/confirm, toggle, adopt) - market: 2 handlers (create/soft-delete marketplace) - cart: 1 handler (adopt cart for user) Data endpoints (reads): - blog: 4 (post-by-slug/id, posts-by-ids, search-posts) - events: 10 (pending entries/tickets, entries/tickets for page/order, entry-ids, associated-entries, calendars, visible-entries-for-period) - market: 1 (marketplaces-for-container) - cart: 1 (cart-summary) Service registration cleanup: - blog→blog+federation, events→calendar+federation, market→market+federation, cart→cart only, federation→federation only, account→nothing - Stubs reduced to minimal StubFederationService Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ from jinja2 import FileSystemLoader, ChoiceLoader
|
||||
|
||||
from shared.infrastructure.factory import create_base_app
|
||||
|
||||
from bp import register_all_events, register_calendars, register_markets, register_payments, register_page, register_fragments
|
||||
from bp import register_all_events, register_calendars, register_markets, register_payments, register_page, register_fragments, register_actions, register_data
|
||||
|
||||
|
||||
async def events_context() -> dict:
|
||||
@@ -20,20 +20,25 @@ async def events_context() -> dict:
|
||||
"""
|
||||
from shared.infrastructure.context import base_context
|
||||
from shared.services.navigation import get_navigation_tree
|
||||
from shared.services.registry import services
|
||||
from shared.infrastructure.cart_identity import current_cart_identity
|
||||
from shared.infrastructure.fragments import fetch_fragments
|
||||
from shared.infrastructure.data_client import fetch_data
|
||||
from shared.contracts.dtos import CartSummaryDTO, dto_from_dict
|
||||
|
||||
ctx = await base_context()
|
||||
|
||||
# Fallback for _nav.html when nav-tree fragment fetch fails
|
||||
ctx["menu_items"] = await get_navigation_tree(g.s)
|
||||
|
||||
# Cart data via service (replaces cross-app HTTP API)
|
||||
# Cart data via internal data endpoint
|
||||
ident = current_cart_identity()
|
||||
summary = await services.cart.cart_summary(
|
||||
g.s, user_id=ident["user_id"], session_id=ident["session_id"],
|
||||
)
|
||||
summary_params = {}
|
||||
if ident["user_id"] is not None:
|
||||
summary_params["user_id"] = ident["user_id"]
|
||||
if ident["session_id"] is not None:
|
||||
summary_params["session_id"] = ident["session_id"]
|
||||
raw = await fetch_data("cart", "cart-summary", params=summary_params, required=False)
|
||||
summary = dto_from_dict(CartSummaryDTO, raw) if raw else CartSummaryDTO()
|
||||
ctx["cart_count"] = summary.count + summary.calendar_count + summary.ticket_count
|
||||
ctx["cart_total"] = float(summary.total + summary.calendar_total + summary.ticket_total)
|
||||
|
||||
@@ -58,7 +63,6 @@ async def events_context() -> dict:
|
||||
|
||||
|
||||
def create_app() -> "Quart":
|
||||
from shared.services.registry import services
|
||||
from services import register_domain_services
|
||||
|
||||
app = create_base_app(
|
||||
@@ -105,6 +109,8 @@ def create_app() -> "Quart":
|
||||
)
|
||||
|
||||
app.register_blueprint(register_fragments())
|
||||
app.register_blueprint(register_actions())
|
||||
app.register_blueprint(register_data())
|
||||
|
||||
# --- Auto-inject slug into url_for() calls ---
|
||||
@app.url_value_preprocessor
|
||||
@@ -122,31 +128,39 @@ def create_app() -> "Quart":
|
||||
# --- Load post data for slug ---
|
||||
@app.before_request
|
||||
async def hydrate_post():
|
||||
from shared.infrastructure.data_client import fetch_data
|
||||
slug = getattr(g, "post_slug", None)
|
||||
if not slug:
|
||||
return
|
||||
post = await services.blog.get_post_by_slug(g.s, slug)
|
||||
post = await fetch_data("blog", "post-by-slug", params={"slug": slug})
|
||||
if not post:
|
||||
abort(404)
|
||||
g.post_data = {
|
||||
"post": {
|
||||
"id": post.id,
|
||||
"title": post.title,
|
||||
"slug": post.slug,
|
||||
"feature_image": post.feature_image,
|
||||
"status": post.status,
|
||||
"visibility": post.visibility,
|
||||
"id": post["id"],
|
||||
"title": post["title"],
|
||||
"slug": post["slug"],
|
||||
"feature_image": post.get("feature_image"),
|
||||
"status": post["status"],
|
||||
"visibility": post["visibility"],
|
||||
},
|
||||
}
|
||||
|
||||
@app.context_processor
|
||||
async def inject_post():
|
||||
from shared.infrastructure.data_client import fetch_data
|
||||
from shared.contracts.dtos import CalendarDTO, MarketPlaceDTO, dto_from_dict
|
||||
from shared.services.registry import services
|
||||
post_data = getattr(g, "post_data", None)
|
||||
if not post_data:
|
||||
return {}
|
||||
post_id = post_data["post"]["id"]
|
||||
# Calendar data is local (events owns it)
|
||||
calendars = await services.calendar.calendars_for_container(g.s, "page", post_id)
|
||||
markets = await services.market.marketplaces_for_container(g.s, "page", post_id)
|
||||
# Market data is cross-app
|
||||
raw_markets = await fetch_data("market", "marketplaces-for-container",
|
||||
params={"type": "page", "id": post_id}, required=False) or []
|
||||
markets = [dto_from_dict(MarketPlaceDTO, m) for m in raw_markets]
|
||||
return {
|
||||
**post_data,
|
||||
"calendars": calendars,
|
||||
@@ -168,6 +182,7 @@ def create_app() -> "Quart":
|
||||
from quart import jsonify
|
||||
from shared.infrastructure.urls import events_url
|
||||
from shared.infrastructure.oembed import build_oembed_response
|
||||
from shared.infrastructure.data_client import fetch_data
|
||||
|
||||
url = request.args.get("url", "")
|
||||
if not url:
|
||||
@@ -178,15 +193,15 @@ def create_app() -> "Quart":
|
||||
if not slug:
|
||||
return jsonify({"error": "could not extract slug"}), 404
|
||||
|
||||
post = await services.blog.get_post_by_slug(g.s, slug)
|
||||
post = await fetch_data("blog", "post-by-slug", params={"slug": slug})
|
||||
if not post:
|
||||
return jsonify({"error": "not found"}), 404
|
||||
|
||||
resp = build_oembed_response(
|
||||
title=post.title,
|
||||
title=post["title"],
|
||||
oembed_type="link",
|
||||
thumbnail_url=post.feature_image,
|
||||
url=events_url(f"/{post.slug}"),
|
||||
thumbnail_url=post.get("feature_image"),
|
||||
url=events_url(f"/{post['slug']}"),
|
||||
)
|
||||
return jsonify(resp)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user