fix(client): download newer-storage attachments through their signed storage url - #22
Merged
Merged
Conversation
…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.
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 (
type11, UUIDid), the authenticated API path/api/v1/cards/{card_uid}/files/{id}does not redirect to storage as #12 described — it answers200 application/jsonwith the file's metadata, whoseurlis a signed storage link (storage.yandexcloud.net,X-Amz-Expires=15).Files::download(and thereforecard file getanddownload_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 signedurlwithout 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_bytesreturns the content type alongside the bytes (crate-private).files[]entry and the metadata answer; the CLI and MCP by-uid tests now drive the real two-step protocol.Verification
-D warnings/cargo test --workspace: 249 passed.cargo semver-checksvs v0.4.0: no semver update required.card file get <card> <uid>→ 33055-byte HTML (was 982 bytes of JSON);-vshowsGET(bytes)on the API host withwith_auth=true, then onstorage.yandexcloud.netwithwith_auth=false; MCPdownload_fileproduces the identical file.Release: patch (0.4.1).
🤖 Generated with Claude Code