feat(api): add /modules/hashes endpoint for drift detection
This commit is contained in:
@@ -88,3 +88,29 @@ async def test_embed_bundle_rejects_non_list_uuids(aiohttp_client, app_with_db):
|
||||
"uuids": "not-a-list",
|
||||
})
|
||||
assert resp.status == 400
|
||||
|
||||
|
||||
async def test_hashes_endpoint_returns_uuid_hash_map_for_wildcards_only(
|
||||
aiohttp_client, app_with_db,
|
||||
):
|
||||
"""Drift-detection primitive. Returns {hashes: {uuid: hash}} keyed
|
||||
by 8hex uuid. Wildcards-only because catalog only contains wildcards
|
||||
(spec §2.7) — no point shipping hashes for kinds that never drift
|
||||
embedded snapshots."""
|
||||
app, conn = app_with_db
|
||||
repo = ModuleRepository(conn)
|
||||
wc = repo.create(
|
||||
type="wildcard", name="x", description="",
|
||||
category_id=None, tags=[], payload={"options": []},
|
||||
)
|
||||
cb = repo.create(
|
||||
type="combine", name="y", description="",
|
||||
category_id=None, tags=[], payload={"template": "$a"},
|
||||
)
|
||||
client = await aiohttp_client(app)
|
||||
resp = await client.get("/wp/api/modules/hashes")
|
||||
assert resp.status == 200
|
||||
body = await resp.json()
|
||||
assert wc["uuid"] in body["hashes"]
|
||||
assert cb["uuid"] not in body["hashes"] # combines excluded
|
||||
assert body["hashes"][wc["uuid"]] == wc["payload_hash"]
|
||||
|
||||
@@ -162,6 +162,19 @@ async def toggle_favorite(request: web.Request) -> web.Response:
|
||||
return json_ok(updated)
|
||||
|
||||
|
||||
async def list_hashes(request: web.Request) -> web.Response:
|
||||
"""Lightweight bulk hash fetch for drift detection. Spec §4.2.
|
||||
|
||||
Returns wildcards-only because catalog never contains other kinds
|
||||
(spec §2.7). Lightweight (no payload, no metadata) so the SPA can
|
||||
poll on every workflow load without measurable cost."""
|
||||
with db_session(request) as conn:
|
||||
rows = ModuleRepository(conn).list(type="wildcard")
|
||||
return json_ok({
|
||||
"hashes": {row["uuid"]: row["payload_hash"] for row in rows},
|
||||
})
|
||||
|
||||
|
||||
async def embed_bundle(request: web.Request) -> web.Response:
|
||||
"""Lazy walk for SPA library picker. Spec §4.2.
|
||||
|
||||
@@ -220,6 +233,7 @@ async def embed_bundle(request: web.Request) -> web.Response:
|
||||
def register(router) -> None:
|
||||
router.add_get("/wp/api/modules", list_modules)
|
||||
router.add_post("/wp/api/modules", create_module)
|
||||
router.add_get("/wp/api/modules/hashes", list_hashes)
|
||||
router.add_post("/wp/api/modules/match", match_module)
|
||||
router.add_post("/wp/api/modules/embed-bundle", embed_bundle)
|
||||
router.add_get("/wp/api/modules/{id}", get_module)
|
||||
|
||||
Reference in New Issue
Block a user