Skip to content

Feature: Read and download project files - #148

Merged
ripexz merged 3 commits into
mainfrom
feature-file-read
Sep 11, 2026
Merged

ripexz merged 3 commits into
mainfrom
feature-file-read

Conversation

@ripexz

@ripexz ripexz commented Sep 10, 2026 •

Copy link
Copy Markdown
Member

Description

Files were write-only in the SDK: FileCreate and FileDelete existed, but nothing could read a file's attributes, list a project's files area or fetch the content. This adds the read side on the v3 routes and a download step.

  • FileGet — GET /projects/api/v3/files/{id}.json, with Version, IncludeVersions, typed Include sideloads (users, projects, tags, tasks) and a generated FileGetFields slot.
  • FileList — GET /projects/api/v3/files.json, or GET /projects/api/v3/projects/{id}/files.json when Path.ProjectID is set. Filters: IDs, task, category, tags, uploaders, search term (optionally across all fields), upload date window, updated-after, deleted files, external files, versions, sideloads, ordering, paging, count mode and sparse fields. Date boundary rules are stated on each field.
  • FileDownload — builds the address File.DownloadURL reports from the session's server, so the session's Bearer token authenticates it the same way it does the v3 endpoints. The route answers 302 to a signed storage URL, which the HTTP client follows; the caller gets the suggested name, content type, size and an open body. FileDownloadResponse deliberately does not implement twapi.HTTPResponser, because twapi.Execute closes the body before returning.
  • File, FileVersion and FileRelatedItems models, plus FileStatus, FileRequestSideload and FileOrderBy enums.
  • A short AGENTS.md section on the download route.
  • Task.Attachments, MessageReply.Attachments and Comment.Files, the file relationships the v3 responses already carry on every row (attachments, attachments and files), so a typed read says which files an item carries and hands FileGet / FileDownload their ID. A message's own attachments arrive on the reply that holds its body.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Testing

  • Tests pass locally (go test -v ./...)
  • Added/updated tests for new functionality

Examples for get, list and download run against a mock that redirects to a storage path as the real route does. Integration tests (TestFileGet, TestFileList, TestFileDownload) were run against a live site with a Bearer token and pass, including the downloaded bytes matching the uploaded fixture. Rows were added to the ordering and sparse-field get tables; go generate output is committed.

Checklist

  • Code follows project style guidelines
  • Self-reviewed the code
  • Added necessary documentation
  • No new warnings or errors

🤖 Generated with Claude Code

@ripexz
ripexz requested a review from a team as a code owner September 10, 2026 15:41
Comment thread projects/file.go Outdated
Comment thread projects/file.go Outdated
Comment thread projects/file.go Outdated
Comment thread projects/file.go Outdated
Comment thread projects/file.go Outdated
Comment thread projects/file.go Outdated
@rafaeljusto

rafaeljusto commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

I will patch AGENTS.md to avoid these redundant fields in future iterations.

EDIT: #149

ripexz and others added 2 commits September 11, 2026 09:11
Add FileGet, FileList and FileDownload to the projects package. Files were
write-only in the SDK: FileCreate and FileDelete existed, but nothing could
read a file's attributes, list a project's files area or fetch the content.

FileGet and FileList use the v3 routes, GET /projects/api/v3/files/{id}.json
and GET /projects/api/v3/files.json (or the project-scoped
/projects/{id}/files.json), with typed sideloads, ordering, count mode and
generated sparse-field slots. FileDownload builds the address that
File.DownloadURL reports from the session's server, so the session's Bearer
token authenticates it, follows the redirect to storage and hands the caller
an open body. Its response type deliberately does not implement
twapi.HTTPResponser, since twapi.Execute closes the body before returning.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Drop FileID and ProjectID from FileVersion, and VersionID, ProjectID,
CategoryID and TagIDs from File: each repeats a relation the neighbouring
twapi.Relationship already carries, and the generated FileField constants
followed them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ripexz

ripexz commented Sep 11, 2026

Copy link
Copy Markdown
Member Author

Rebased onto main and dropped the six duplicated identifiers (FileVersion.FileID, FileVersion.ProjectID, File.VersionID, File.ProjectID, File.CategoryID, File.TagIDs) in favour of the twapi.Relationship fields, per the new One field per relationship rule. Sparse-field constants regenerated.

The v3 responses carry them on every row as file relationships: tasks and
message replies under "attachments", comments under "files". The models
dropped the key, so a typed read could not say which files an item carried,
and the ID is what FileGet and FileDownload take.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ripexz

ripexz commented Sep 11, 2026

Copy link
Copy Markdown
Member Author

Added the file relationships the v3 responses already carry, so the attached files of an item can be read back and handed to FileGet / FileDownload: Task.Attachments and MessageReply.Attachments (attachments), and Comment.Files (files — the response also spells it as fileIds, which is left out under the one-field-per-relationship rule). Sparse-field constants regenerated; the PR description lists them.

@ripexz
ripexz merged commit c947f99 into main Sep 11, 2026
4 of 5 checks passed
@ripexz
ripexz deleted the feature-file-read branch September 11, 2026 13:44
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.

2 participants