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>
75 lines
2.3 KiB
Python
75 lines
2.3 KiB
Python
"""Blog app data endpoints.
|
|
|
|
Exposes read-only JSON queries at ``/internal/data/<query_name>`` for
|
|
cross-app callers via the internal data client.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from quart import Blueprint, g, jsonify, request
|
|
|
|
from shared.infrastructure.data_client import DATA_HEADER
|
|
from shared.contracts.dtos import dto_to_dict
|
|
from shared.services.registry import services
|
|
|
|
|
|
def register() -> Blueprint:
|
|
bp = Blueprint("data", __name__, url_prefix="/internal/data")
|
|
|
|
@bp.before_request
|
|
async def _require_data_header():
|
|
if not request.headers.get(DATA_HEADER):
|
|
return jsonify({"error": "forbidden"}), 403
|
|
|
|
_handlers: dict[str, object] = {}
|
|
|
|
@bp.get("/<query_name>")
|
|
async def handle_query(query_name: str):
|
|
handler = _handlers.get(query_name)
|
|
if handler is None:
|
|
return jsonify({"error": "unknown query"}), 404
|
|
result = await handler()
|
|
return jsonify(result)
|
|
|
|
# --- post-by-slug ---
|
|
async def _post_by_slug():
|
|
slug = request.args.get("slug", "")
|
|
post = await services.blog.get_post_by_slug(g.s, slug)
|
|
if not post:
|
|
return None
|
|
return dto_to_dict(post)
|
|
|
|
_handlers["post-by-slug"] = _post_by_slug
|
|
|
|
# --- post-by-id ---
|
|
async def _post_by_id():
|
|
post_id = int(request.args.get("id", 0))
|
|
post = await services.blog.get_post_by_id(g.s, post_id)
|
|
if not post:
|
|
return None
|
|
return dto_to_dict(post)
|
|
|
|
_handlers["post-by-id"] = _post_by_id
|
|
|
|
# --- posts-by-ids ---
|
|
async def _posts_by_ids():
|
|
ids_raw = request.args.get("ids", "")
|
|
if not ids_raw:
|
|
return []
|
|
ids = [int(x.strip()) for x in ids_raw.split(",") if x.strip()]
|
|
posts = await services.blog.get_posts_by_ids(g.s, ids)
|
|
return [dto_to_dict(p) for p in posts]
|
|
|
|
_handlers["posts-by-ids"] = _posts_by_ids
|
|
|
|
# --- search-posts ---
|
|
async def _search_posts():
|
|
query = request.args.get("query", "")
|
|
page = int(request.args.get("page", 1))
|
|
per_page = int(request.args.get("per_page", 10))
|
|
posts, total = await services.blog.search_posts(g.s, query, page, per_page)
|
|
return {"posts": [dto_to_dict(p) for p in posts], "total": total}
|
|
|
|
_handlers["search-posts"] = _search_posts
|
|
|
|
return bp
|