feat(13-06): route upload success through centralized reconciliation before returning

- Upsert uploaded item into cloud_items for stable row identity and navigation
- Invalidate parent folder state (warning/upload_mutated) so next browse re-fetches
- Reconcile-before-return: session committed before response returned
- Failed/conflict uploads bypass reconciliation path (T-13-19)
This commit is contained in:
curo1305
2026-06-22 19:26:41 +02:00
parent 4cd6499a96
commit 7ecbec7df9
+52 -4
View File
@@ -637,12 +637,60 @@ async def upload_cloud_file(
kind = result.get("kind")
if kind == MUT_KIND_UPLOADED:
# Route authoritative upload success through centralized reconciliation
# before returning (Plan 06, Task 1: T-13-19).
# upsert_cloud_item gives the new item stable row identity in cloud_items,
# so navigation can show it immediately without a full provider re-list.
# update_folder_state invalidates the parent folder so the next browse
# triggers a provider re-list and reflects the new content truthfully.
# These calls are intentionally in-request — the response is not returned
# until reconciliation completes (reconcile-before-return contract).
provider_item_id = result.get("provider_item_id")
resolved_name = result.get("name", upload_filename)
resolved_parent_ref = result.get("parent_ref", parent_ref)
resolved_size = result.get("size", len(content))
if provider_item_id:
from storage.cloud_base import CloudResource
from services.cloud_items import upsert_cloud_item, update_folder_state
resource = CloudResource(
id=uuid.uuid4(),
provider_item_id=provider_item_id,
connection_id=connection_id,
user_id=current_user.id,
name=resolved_name,
kind="file",
parent_ref=resolved_parent_ref,
content_type=content_type,
size=resolved_size,
)
# Upsert item — establishes stable row identity for navigation
await upsert_cloud_item(session, user_id=str(current_user.id), resource=resource)
# Invalidate parent folder so next browse triggers a provider re-list.
# We do NOT call apply_listing_and_finalize — that requires a full provider
# listing which we don't have. Instead we mark the folder stale with a
# controlled reason so the browse endpoint knows to re-fetch.
invalidate_parent = resolved_parent_ref if resolved_parent_ref is not None else ""
await update_folder_state(
session,
user_id=str(current_user.id),
connection_id=str(connection_id),
parent_ref=invalidate_parent,
refresh_state="warning",
error_code="upload_mutated",
error_message="Folder contents changed by upload — re-listing required.",
)
await session.commit()
return {
"kind": "uploaded",
"provider_item_id": result.get("provider_item_id"),
"name": result.get("name", upload_filename),
"parent_ref": result.get("parent_ref", parent_ref),
"size": result.get("size", len(content)),
"provider_item_id": provider_item_id,
"name": resolved_name,
"parent_ref": resolved_parent_ref,
"size": resolved_size,
}
if kind == MUT_KIND_CONFLICT: