feat(12.1-02): centralize listing freshness gate — apply_listing_and_finalize
- Add ListingResult dataclass to cloud_items.py
- Add apply_listing_and_finalize() — single shared gate for complete/incomplete semantics
- complete=True: upserts, soft-deletes unseen children, marks fresh, advances last_refreshed_at
- complete=False: upserts seen items only, never deletes unseen, preserves prior timestamp, warns
- browse.py: replace unconditional update_folder_state('fresh') with apply_listing_and_finalize
- cloud_tasks.py: replace unconditional fresh transition with apply_listing_and_finalize
- Celery task returns structured warning status on incomplete (no credentials/provider item names)
- Fix timezone naive/aware comparison in test (SQLite strips tzinfo)
This commit is contained in:
@@ -59,6 +59,7 @@ async def _run(user_id: str, connection_id: str, parent_ref: str | None) -> dict
|
||||
from db.session import AsyncSessionLocal
|
||||
from services.cloud_items import (
|
||||
ConnectionNotFound,
|
||||
apply_listing_and_finalize,
|
||||
get_or_create_folder_state,
|
||||
reconcile_cloud_listing,
|
||||
resolve_owned_connection,
|
||||
@@ -137,25 +138,28 @@ async def _run(user_id: str, connection_id: str, parent_ref: str | None) -> dict
|
||||
# Transient failure — let Celery retry
|
||||
raise _TransientProviderError("Provider error. Will retry.") from exc
|
||||
|
||||
# Reconcile listing into durable cloud_items rows
|
||||
await reconcile_cloud_listing(
|
||||
# apply_listing_and_finalize is the single shared gate — it handles
|
||||
# complete vs. incomplete semantics; only a complete authoritative
|
||||
# listing transitions the folder to fresh state.
|
||||
listing_result = await apply_listing_and_finalize(
|
||||
session,
|
||||
user_id=user_id,
|
||||
connection_id=connection_id,
|
||||
parent_ref=parent_ref,
|
||||
listing=listing,
|
||||
)
|
||||
|
||||
await update_folder_state(
|
||||
session,
|
||||
user_id=user_id,
|
||||
connection_id=connection_id,
|
||||
parent_ref=parent_ref or "",
|
||||
refresh_state="fresh",
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
return {"status": "ok", "items_fetched": len(listing.items)}
|
||||
if listing_result.is_fresh:
|
||||
return {"status": "ok", "items_fetched": len(listing.items)}
|
||||
else:
|
||||
# Incomplete listing: task succeeded but folder is not fresh.
|
||||
# Return a controlled status — no credentials, no provider item names.
|
||||
return {
|
||||
"status": "warning",
|
||||
"warning_code": listing_result.warning_code,
|
||||
"items_fetched": len(listing.items),
|
||||
}
|
||||
|
||||
|
||||
# ── Celery task ───────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user