Skip to content

feat(im): use Content-Disposition filename when downloading message r…#525

Closed
chenxingtong-bytedance wants to merge 1 commit intolarksuite:mainfrom
chenxingtong-bytedance:fix/im-content-disposition-filename
Closed

feat(im): use Content-Disposition filename when downloading message r…#525
chenxingtong-bytedance wants to merge 1 commit intolarksuite:mainfrom
chenxingtong-bytedance:fix/im-content-disposition-filename

Conversation

@chenxingtong-bytedance
Copy link
Copy Markdown
Contributor

@chenxingtong-bytedance chenxingtong-bytedance commented Apr 16, 2026

Summary

When downloading message resources, the saved filename was always derived from
file_key (e.g. file_v2_abc123.xlsx), ignoring the original filename the
sender uploaded. This PR resolves filenames from the Content-Disposition
response header first, falling back to Content-Type-based extension inference
only when the header is absent.

Changes

  • Add parseContentDispositionFilename to extract and sanitize filenames from
    Content-Disposition, with RFC 5987 (filename*=UTF-8''...) support via the
    standard mime package
  • Update resolveIMResourceDownloadPath to prioritise Content-Disposition
    filename; when --output is not specified, the full server filename is used;
    when --output is specified without an extension, only the extension is taken
    from the Content-Disposition filename
  • Add userSpecifiedOutput bool parameter to downloadIMResourceToPath and
    resolveIMResourceDownloadPath to distinguish default vs. explicit output paths
  • Update skills/lark-im/references/lark-im-messages-resources-download.md to
    reflect the new filename resolution priority

Test Plan

  • go test ./shortcuts/im/... passed
  • Added TestParseContentDispositionFilename (11 cases): plain filename,

Summary by CodeRabbit

  • New Features

    • Downloads now prefer server-provided filenames (including RFC5987 UTF‑8) when no output path is given; if a user-specified output lacks an extension, a suitable extension will be appended.
  • Bug Fixes

    • Better handling and clearer error reporting for empty/failed download responses.
  • Tests

    • Added tests for filename parsing, output-path resolution, and varied download scenarios.
  • Documentation

    • Docs updated to reflect the new filename/extension resolution behavior.

@github-actions github-actions Bot added domain/im PR touches the im domain size/M Single-domain feat or fix with limited business impact labels Apr 16, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 16, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Adds Content-Disposition filename parsing and a userSpecifiedOutput flag to IM resource downloads; download path resolution now prefers server-provided filenames/extensions (RFC 5987-aware), returns a clear error on nil responses, and updates tests to pass the new flag and cover filename parsing and path-resolution cases.

Changes

Cohort / File(s) Summary
Tests
shortcuts/im/helpers_network_test.go, shortcuts/im/helpers_test.go
Updated calls to downloadIMResourceToPath to include new userSpecifiedOutput boolean; added tests TestParseContentDispositionFilename and TestResolveIMResourceDownloadPath to validate filename extraction, sanitization, and resolved output path logic.
Download logic
shortcuts/im/im_messages_resources_download.go
Added parseContentDispositionFilename; downloadIMResourceToPath now accepts userSpecifiedOutput bool and returns an explicit error when response is nil; resolveIMResourceDownloadPath accepts contentDisposition and userSpecifiedOutput and prefers Content-Disposition filename/extension before falling back to Content-Type.
Documentation
skills/lark-im/references/lark-im-messages-resources-download.md
Updated --output behavior and extension-derivation docs to reflect Content-Disposition filename preservation (including RFC 5987) and new fallback order.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as Download Handler
    participant HTTP as HTTP Client
    participant Parser as Filename Resolver
    participant FS as File System

    User->>CLI: Execute (resource URL, --output?)
    CLI->>CLI: set userSpecifiedOutput flag
    CLI->>HTTP: doIMResourceDownloadRequest()
    HTTP-->>CLI: Response (headers: Content-Disposition, Content-Type, body)
    CLI->>CLI: if response == nil -> return error
    CLI->>Parser: parseContentDispositionFilename(Content-Disposition)
    Parser-->>CLI: sanitizedFilename
    CLI->>CLI: resolveIMResourceDownloadPath(safePath, sanitizedFilename, contentType, userSpecifiedOutput)
    CLI-->>FS: write file to resolved path
    FS-->>CLI: success / error
    CLI-->>User: complete / error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • jackie3927
  • liangshuo-1
  • YangJunzhou-01

Poem

🐰 I nibble headers, tidy names with care,

I fetch your bytes and trim each snare,
Filename saved from server's song,
Extensions matched, no path goes wrong,
Hopping home — downloads done with flair. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly identifies the main feature: using Content-Disposition filename when downloading message resources, which accurately reflects the primary change in the changeset.
Description check ✅ Passed The PR description covers the summary, main changes, and test plan sections from the template. However, the test plan section appears to be incomplete (cut off mid-sentence) and the 'Related Issues' section is missing entirely.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@shortcuts/im/helpers_test.go`:
- Around line 668-700: Add an end-to-end test that covers the default-output
branch by invoking downloadIMResourceToPath with userSpecifiedOutput=false and a
mocked HTTP response that includes a Content-Disposition header with a filename
(e.g., `attachment; filename="server.pdf"`); ensure the test uses the same
helper setup as other downloads, verifies the file is actually written to disk
under the server-provided name (not the client safePath), and cleans up the file
afterwards. Reference downloadIMResourceToPath and resolveIMResourceDownloadPath
when locating where to wire the test; assert the saved path equals the expected
filename from the Content-Disposition and fail the test on mismatch. Ensure the
test mirrors existing download test structure (setup/mocks/teardown) so it
exercises the real save flow rather than only calling
resolveIMResourceDownloadPath.

In `@skills/lark-im/references/lark-im-messages-resources-download.md`:
- Line 37: Update the documentation for the `--output` option to reflect the
actual fallback chain used by resolveIMResourceDownloadPath: first use the
server filename from Content-Disposition if present; otherwise use the
resource's file_key and, when available, append an extension inferred from
Content-Type (e.g., saving as file_key.pdf), rather than saying it defaults to a
bare file_key. Mention Content-Disposition and Content-Type explicitly and
reference resolveIMResourceDownloadPath and file_key so readers know where the
behavior originates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fd39fc6a-114b-430c-8cb6-58820d9f902e

📥 Commits

Reviewing files that changed from the base of the PR and between c442fa2 and 9cf4659.

📒 Files selected for processing (4)
  • shortcuts/im/helpers_network_test.go
  • shortcuts/im/helpers_test.go
  • shortcuts/im/im_messages_resources_download.go
  • skills/lark-im/references/lark-im-messages-resources-download.md

Comment on lines +668 to +700
func TestResolveIMResourceDownloadPath(t *testing.T) {
tests := []struct {
name string
safePath string
contentType string
contentDisposition string
userSpecifiedOutput bool
want string
}{
// safePath already has extension: always return as-is
{name: "user path with ext, no CD", safePath: "out.xlsx", contentType: "application/pdf", userSpecifiedOutput: true, want: "out.xlsx"},
{name: "user path with ext, CD present", safePath: "out.xlsx", contentDisposition: `attachment; filename="server.pdf"`, userSpecifiedOutput: true, want: "out.xlsx"},
// No --output: use CD filename when present
{name: "default path, CD filename", safePath: "file_xxx", contentDisposition: `attachment; filename="季度报告.xlsx"`, want: "季度报告.xlsx"},
{name: "default path, CD RFC5987", safePath: "file_xxx", contentDisposition: `attachment; filename*=UTF-8''%E5%AD%A3%E5%BA%A6%E6%8A%A5%E5%91%8A.xlsx`, want: "季度报告.xlsx"},
{name: "default path, no CD, MIME ext", safePath: "file_xxx", contentType: "application/pdf", want: "file_xxx.pdf"},
{name: "default path, no CD, unknown MIME", safePath: "file_xxx", contentType: "application/x-unknown", want: "file_xxx"},
{name: "default path, CD with dir component", safePath: "downloads/file_xxx", contentDisposition: `attachment; filename="report.xlsx"`, want: "downloads/report.xlsx"},
// User --output without extension: use CD filename's extension
{name: "user path no ext, CD with ext", safePath: "myfile", contentDisposition: `attachment; filename="server.pdf"`, userSpecifiedOutput: true, want: "myfile.pdf"},
{name: "user path no ext, CD no ext, MIME ext", safePath: "myfile", contentDisposition: `attachment; filename="noext"`, contentType: "image/png", userSpecifiedOutput: true, want: "myfile.png"},
{name: "user path no ext, no CD, MIME ext", safePath: "myfile", contentType: "image/jpeg", userSpecifiedOutput: true, want: "myfile.jpg"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := resolveIMResourceDownloadPath(tt.safePath, tt.contentType, tt.contentDisposition, tt.userSpecifiedOutput)
if got != tt.want {
t.Fatalf("resolveIMResourceDownloadPath() = %q, want %q", got, tt.want)
}
})
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Please cover the default-output branch end-to-end as well.

These helper tests are useful, but they still don’t exercise the new downloadIMResourceToPath(..., userSpecifiedOutput=false) path. Right now every download test in the provided suite passes true, so a regression in wiring Content-Disposition through the actual save flow would still pass. Please add one download test that returns a Content-Disposition filename, passes false, and asserts the file is saved under that server name.

As per coding guidelines, “Every behavior change must have an accompanying test”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/im/helpers_test.go` around lines 668 - 700, Add an end-to-end test
that covers the default-output branch by invoking downloadIMResourceToPath with
userSpecifiedOutput=false and a mocked HTTP response that includes a
Content-Disposition header with a filename (e.g., `attachment;
filename="server.pdf"`); ensure the test uses the same helper setup as other
downloads, verifies the file is actually written to disk under the
server-provided name (not the client safePath), and cleans up the file
afterwards. Reference downloadIMResourceToPath and resolveIMResourceDownloadPath
when locating where to wire the test; assert the saved path equals the expected
filename from the Content-Disposition and fail the test on mismatch. Ensure the
test mirrors existing download test structure (setup/mocks/teardown) so it
exercises the real save flow rather than only calling
resolveIMResourceDownloadPath.

Comment thread skills/lark-im/references/lark-im-messages-resources-download.md
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@shortcuts/im/im_messages_resources_download.go`:
- Around line 389-390: Add a unit test that calls doIMResourceDownloadRequest
and asserts that every outgoing request includes the PPE headers "x-tt-env:
ppe_resource_download_file" and "x-use-ppe: 1"; implement the test using an
httptest.Server to capture requests, invoke doIMResourceDownloadRequest (from
shortcuts/im/im_messages_resources_download.go) for at least two different
fileType values and any other varying parameters, and fail if the captured
request headers do not contain the exact header names and values; name the test
clearly (e.g., TestDoIMResourceDownloadRequest_IncludesPPEHeaders) and ensure it
runs as part of the package tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a7b2903a-04f8-4afe-82f6-8ec05ebcf07a

📥 Commits

Reviewing files that changed from the base of the PR and between 9cf4659 and 07ee8ca.

📒 Files selected for processing (1)
  • shortcuts/im/im_messages_resources_download.go

Comment thread shortcuts/im/im_messages_resources_download.go Outdated
…esources

Change-Id: I68b48cf428aa8aded4ad9d55fa042f9d68263c3a
@chenxingtong-bytedance chenxingtong-bytedance force-pushed the fix/im-content-disposition-filename branch from 07ee8ca to 7328283 Compare April 17, 2026 06:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/im PR touches the im domain size/M Single-domain feat or fix with limited business impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant