Skip to content

fix(client): download newer-storage attachments through their signed storage url - #22

Merged
dsociative merged 3 commits into
masterfrom
fix/12-newer-storage-download
Aug 27, 2026
Merged

fix(client): download newer-storage attachments through their signed storage url#22
dsociative merged 3 commits into
masterfrom
fix/12-newer-storage-download

Conversation

@dsociative

Copy link
Copy Markdown
Owner

Fixes a defect in the 0.4.0 feature from #12 (found while verifying it read-only on a real card with a newer-storage attachment).

Problem

For files on Kaiten's newer storage (type 11, UUID id), the authenticated API path /api/v1/cards/{card_uid}/files/{id} does not redirect to storage as #12 described — it answers 200 application/json with the file's metadata, whose url is a signed storage link (storage.yandexcloud.net, X-Amz-Expires=15). Files::download (and therefore card file get and download_file) saved that ~1 KB JSON as the attachment.

Fix (TDD: the two client tests were written first and failed on 0.4.0)

  • Files::download: when the resolved url is on the API origin and the answer is JSON, decode the metadata and immediately fetch its signed url without credentials; any other answer is returned as the bytes it is (a redirecting instance keeps working). The token still goes only to the API origin.
  • KaitenClient::get_bytes returns the content type alongside the bytes (crate-private).
  • Fixtures replaced by live, sanitized captures of both the card files[] entry and the metadata answer; the CLI and MCP by-uid tests now drive the real two-step protocol.

Verification

  • fmt / clippy -D warnings / cargo test --workspace: 249 passed. cargo semver-checks vs v0.4.0: no semver update required.
  • Read-only on the real card: card file get <card> <uid> → 33055-byte HTML (was 982 bytes of JSON); -v shows GET(bytes) on the API host with with_auth=true, then on storage.yandexcloud.net with with_auth=false; MCP download_file produces the identical file.

Release: patch (0.4.1).

🤖 Generated with Claude Code

…storage url

Observed live on a card with a `type` 11 attachment: the authenticated
API path `/api/v1/cards/{card_uid}/files/{id}` does not redirect to
storage — it answers 200 with the file's JSON metadata, whose `url` is a
signed storage link valid for about fifteen seconds. `Files::download`
saved that metadata as the file. It now recognises a JSON answer on the
API origin, decodes the metadata and immediately fetches the signed url
without credentials (the token still goes only to the API origin); any
other answer is returned as the bytes it is, so a redirecting instance
keeps working. `get_bytes` reports the content type for this.

Fixtures are replaced by the live (sanitized) shapes: the card `files[]`
entry and the metadata answer. Client, CLI and MCP tests drive the
two-step protocol; the two client tests were written first and failed
against the previous code.
…f served it (review)

The metadata check keyed on the requested url's origin, but the content
type belongs to the final response: on a redirecting instance a JSON
attachment reached through a 302 would have been parsed as metadata and
its `url` followed. `get_bytes` now reports the url that answered, and
`Files::download` decodes metadata only when that url is the one asked
for, on the API origin, with a JSON media type (compared case-insensitively,
parameters ignored). A refused signed link is reported as such, naming the
file. Tests: JSON attachment behind a redirect and a classic `.json` are
returned verbatim, uppercase media type recognised, invalid metadata is a
decode error, the MCP test fetches the signed url from a second server
without the token.
…adata from the file (review)

reqwest never sends a fragment, so the url that answers never carries one;
comparing against the resolved url with its fragment would have read the
metadata as the file. Also pins that a same-origin redirect to JSON is
returned verbatim.
@dsociative
dsociative merged commit 9838b19 into master Aug 27, 2026
6 checks passed
@dsociative
dsociative deleted the fix/12-newer-storage-download branch August 27, 2026 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant