fix(13): CR-03 wrap post-upload DB block in try/except to handle provider-success/DB-fail divergence
This commit is contained in:
@@ -1043,47 +1043,72 @@ async def upload_cloud_file(
|
||||
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.",
|
||||
)
|
||||
# DB mutation block: upsert + folder-state invalidation + audit + commit.
|
||||
# CR-03: Wrapped in try/except so that a DB failure after a successful
|
||||
# provider upload is reported distinctly rather than propagating as an
|
||||
# unhandled 500. The file IS on the provider; the metadata recording
|
||||
# failed. The frontend receives a typed error so it can surface the
|
||||
# discrepancy rather than silently losing the upload record.
|
||||
try:
|
||||
# Upsert item — establishes stable row identity for navigation
|
||||
await upsert_cloud_item(session, user_id=str(current_user.id), resource=resource)
|
||||
|
||||
# Write metadata-only audit row in the same transaction (Plan 06, Task 2: T-13-20).
|
||||
# Only authoritative upload success reaches this path. Non-success outcomes
|
||||
# (conflict, offline, reauth_required) are handled in separate branches below
|
||||
# and never call write_audit_log — preserving T-13-21 (no false success events).
|
||||
# The audit payload is metadata-only: no provider URLs, tokens, bytes, or
|
||||
# document text are included (T-13-02).
|
||||
await write_audit_log(
|
||||
session,
|
||||
event_type="cloud.file_uploaded",
|
||||
user_id=current_user.id,
|
||||
actor_id=current_user.id,
|
||||
resource_id=None,
|
||||
ip_address=get_client_ip(request),
|
||||
metadata_={
|
||||
"connection_id": str(connection_id),
|
||||
"provider_item_id": provider_item_id,
|
||||
"filename": resolved_name,
|
||||
"size_bytes": resolved_size,
|
||||
"parent_ref": resolved_parent_ref,
|
||||
},
|
||||
)
|
||||
# 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()
|
||||
# Write metadata-only audit row in the same transaction (Plan 06, Task 2: T-13-20).
|
||||
# Only authoritative upload success reaches this path. Non-success outcomes
|
||||
# (conflict, offline, reauth_required) are handled in separate branches below
|
||||
# and never call write_audit_log — preserving T-13-21 (no false success events).
|
||||
# The audit payload is metadata-only: no provider URLs, tokens, bytes, or
|
||||
# document text are included (T-13-02).
|
||||
await write_audit_log(
|
||||
session,
|
||||
event_type="cloud.file_uploaded",
|
||||
user_id=current_user.id,
|
||||
actor_id=current_user.id,
|
||||
resource_id=None,
|
||||
ip_address=get_client_ip(request),
|
||||
metadata_={
|
||||
"connection_id": str(connection_id),
|
||||
"provider_item_id": provider_item_id,
|
||||
"filename": resolved_name,
|
||||
"size_bytes": resolved_size,
|
||||
"parent_ref": resolved_parent_ref,
|
||||
},
|
||||
)
|
||||
|
||||
await session.commit()
|
||||
except Exception:
|
||||
# Provider upload succeeded but DB recording failed.
|
||||
# Roll back any partial writes and return a typed error so the frontend
|
||||
# knows the file is on the provider but DocuVault has no metadata record.
|
||||
await session.rollback()
|
||||
return JSONResponse(
|
||||
status_code=status.HTTP_207_MULTI_STATUS,
|
||||
content={
|
||||
"kind": "provider_success_db_error",
|
||||
"reason": "metadata_record_failed",
|
||||
"provider_item_id": provider_item_id,
|
||||
"message": (
|
||||
"File uploaded to provider but DocuVault metadata recording failed. "
|
||||
"Refresh the folder to re-sync."
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
return {
|
||||
"kind": "uploaded",
|
||||
|
||||
Reference in New Issue
Block a user