Skip to content

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

Merged
YangJunzhou-01 merged 1 commit intolarksuite:mainfrom
chenxingtong-bytedance:fix/filename_adapt
Apr 22, 2026
Merged

feat(im): use Content-Disposition filename when downloading message r…#536
YangJunzhou-01 merged 1 commit intolarksuite:mainfrom
chenxingtong-bytedance:fix/filename_adapt

Conversation

@chenxingtong-bytedance
Copy link
Copy Markdown
Contributor

@chenxingtong-bytedance chenxingtong-bytedance commented Apr 17, 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 and preserve server-provided filenames (including RFC 5987 UTF-8), and append or infer extensions intelligently; behavior respects whether an explicit output path was provided.
  • Bug Fixes

    • Better handling of unexpected/missing HTTP responses, clearer error propagation, improved retry/cancellation behavior, and robust cleanup for partial/size-mismatched downloads.
  • Tests

    • Expanded coverage for filename parsing, output-path resolution, range/chunk downloads, errors, retries, and cancellation.
  • Documentation

    • Updated --output docs to match 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 17, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d96c73c0-5d5f-4617-a5cf-9172f04a9623

📥 Commits

Reviewing files that changed from the base of the PR and between f51a3f8 and cfec997.

📒 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
✅ Files skipped from review due to trivial changes (1)
  • skills/lark-im/references/lark-im-messages-resources-download.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • shortcuts/im/helpers_network_test.go
  • shortcuts/im/helpers_test.go
  • shortcuts/im/im_messages_resources_download.go

📝 Walkthrough

Walkthrough

Adds Content-Disposition filename parsing (including RFC 5987), threads a userSpecifiedOutput flag from the CLI into download logic, changes final-path resolution to prefer/disambiguate Content-Disposition and Content-Type, updates download function signature and nil-response handling, adjusts tests, and updates docs.

Changes

Cohort / File(s) Summary
Tests
shortcuts/im/helpers_network_test.go, shortcuts/im/helpers_test.go
Updated test calls to pass new userSpecifiedOutput bool; added tests for parseContentDispositionFilename and resolveIMResourceDownloadPath (RFC5987, sanitization, extension inference, user-specified output cases).
Download implementation
shortcuts/im/im_messages_resources_download.go
Thread userSpecifiedOutput (from --output) into downloadIMResourceToPath; add nil-response network error handling; add parseContentDispositionFilename; update resolveIMResourceDownloadPath to use Content-Disposition when available and fall back to Content-Type; adjust extension-preservation/append rules based on userSpecifiedOutput.
Documentation
skills/lark-im/references/lark-im-messages-resources-download.md
Update --output parameter docs: prefer server Content-Disposition filename when omitted (RFC5987 supported); clarify extension inference order (Content-Disposition then Content-Type).

Sequence Diagram

sequenceDiagram
    actor User as User/CLI
    participant Handler as DownloadHandler
    participant HTTP as HTTPClient
    participant Parser as ContentDispositionParser
    participant Resolver as PathResolver
    User->>Handler: Request download (with/without --output)
    Handler->>Handler: Determine userSpecifiedOutput
    Handler->>HTTP: Perform HTTP request
    HTTP-->>Handler: Response + headers
    Handler->>Parser: Parse Content-Disposition (RFC5987)
    Parser-->>Handler: Sanitized filename or empty
    Handler->>Resolver: Resolve final path (safePath, userSpecifiedOutput, disposition, content-type)
    Resolver-->>Handler: Final output path
    Handler->>HTTP: Download content (with/without Range)
    HTTP-->>Handler: Success / Error
    Handler-->>User: Return result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • jackie3927
  • liangshuo-1
  • fangshuyu-768

Poem

🐰 I found a name upon the stream,
Content-Disposition caught my dream,
RFC5987 sang soft and bright,
Paths made safe and extensions right,
I hop away with files in flight.

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: using Content-Disposition filename for message resource downloads instead of file_key.
Description check ✅ Passed The description covers all required sections: Summary explains the motivation, Changes lists main modifications, and Test Plan confirms testing was done.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
shortcuts/im/im_messages_resources_download.go (1)

35-35: ⚠️ Potential issue | 🟡 Minor

Flag description is stale after this change.

The --output description still states defaults to file_key, but the new resolution logic prefers the Content-Disposition filename when --output is omitted. Consider refreshing the help text so users understand the new default.

✏️ Proposed wording
-		{Name: "output", Desc: "local save path (relative only, no .. traversal; defaults to file_key)"},
+		{Name: "output", Desc: "local save path (relative only, no .. traversal; defaults to server filename from Content-Disposition, or file_key)"},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/im/im_messages_resources_download.go` at line 35, Update the stale
flag description for the "output" CLI flag (Name: "output") to reflect the new
resolution order: state that when omitted the tool will prefer the filename from
the HTTP Content-Disposition header and fall back to the file_key, while still
requiring a relative path with no ".." traversal; change the Desc string where
"output" is defined to this new concise wording.
🧹 Nitpick comments (3)
shortcuts/im/helpers_test.go (2)

668-700: LGTM on TestResolveIMResourceDownloadPath.

Matrix covers the main axes: safePath-with-ext (idempotent), default vs user-specified output, CD-present vs absent, RFC 5987, and MIME fallback. One gap worth adding: the precedence case where userSpecifiedOutput=false, safePath has no extension, CD is absent, and contentType is empty — the expected outcome is that safePath is returned unchanged. Optional to add.

🤖 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 a unit test case to
TestResolveIMResourceDownloadPath covering the missing precedence: call
resolveIMResourceDownloadPath with safePath having no extension (e.g.,
"file_xxx"), contentType == "" and contentDisposition == "" and
userSpecifiedOutput == false, and assert it returns the original safePath
unchanged; update the tests slice in shortcuts/im/helpers_test.go where other
cases are defined so this new case (name like "default path, no CD, no MIME")
sits alongside the existing entries referencing resolveIMResourceDownloadPath.

640-666: Test coverage for parseContentDispositionFilename is thorough.

Good spread across plain/quoted, RFC 5987, precedence, traversal, Windows paths, control chars, malformed, and whitespace. Consider adding one more case to lock in behavior for a filename that is ONLY dots after sanitization (e.g., filename="../..") to ensure it returns "" rather than "..".

✏️ Optional additional case
 		{name: "whitespace trimmed", header: `attachment; filename="  report.pdf  "`, want: "report.pdf"},
+		{name: "dots-only after strip", header: `attachment; filename="../.."`, want: ""},
🤖 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 640 - 666, Add a test case to
TestParseContentDispositionFilename that covers a filename composed only of dots
after sanitization (e.g., header `attachment; filename="../.."`) and assert
parseContentDispositionFilename returns an empty string; this ensures
parseContentDispositionFilename correctly treats names that reduce to "." or
".." as invalid/empty after path-strip sanitization.
shortcuts/im/im_messages_resources_download.go (1)

324-347: Resolution precedence looks correct; one minor observation.

When !userSpecifiedOutput, safePath is the validated file_key (no separators enforced by normalizeDownloadOutputPath), so filepath.Dir(safePath) is always "." in production. The dir != "." branch on lines 334–335 is only exercised by the unit test's synthetic "downloads/file_xxx" input. Not a bug — just noting the branch is effectively dead for the real default flow; you may want to simplify to return cdFilename and drop the join (or keep it as defensive code for future callers).

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

In `@shortcuts/im/im_messages_resources_download.go` around lines 324 - 347, In
resolveIMResourceDownloadPath, simplify the !userSpecifiedOutput branch: because
safePath is the normalized file_key whose filepath.Dir is always ".", return
cdFilename directly instead of computing dir and conditionally joining; remove
the dir/Join logic (or keep a minimal defensive comment if you prefer) so the
function returns cdFilename when cdFilename != "" and !userSpecifiedOutput.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@shortcuts/im/im_messages_resources_download.go`:
- Line 35: Update the stale flag description for the "output" CLI flag (Name:
"output") to reflect the new resolution order: state that when omitted the tool
will prefer the filename from the HTTP Content-Disposition header and fall back
to the file_key, while still requiring a relative path with no ".." traversal;
change the Desc string where "output" is defined to this new concise wording.

---

Nitpick comments:
In `@shortcuts/im/helpers_test.go`:
- Around line 668-700: Add a unit test case to TestResolveIMResourceDownloadPath
covering the missing precedence: call resolveIMResourceDownloadPath with
safePath having no extension (e.g., "file_xxx"), contentType == "" and
contentDisposition == "" and userSpecifiedOutput == false, and assert it returns
the original safePath unchanged; update the tests slice in
shortcuts/im/helpers_test.go where other cases are defined so this new case
(name like "default path, no CD, no MIME") sits alongside the existing entries
referencing resolveIMResourceDownloadPath.
- Around line 640-666: Add a test case to TestParseContentDispositionFilename
that covers a filename composed only of dots after sanitization (e.g., header
`attachment; filename="../.."`) and assert parseContentDispositionFilename
returns an empty string; this ensures parseContentDispositionFilename correctly
treats names that reduce to "." or ".." as invalid/empty after path-strip
sanitization.

In `@shortcuts/im/im_messages_resources_download.go`:
- Around line 324-347: In resolveIMResourceDownloadPath, simplify the
!userSpecifiedOutput branch: because safePath is the normalized file_key whose
filepath.Dir is always ".", return cdFilename directly instead of computing dir
and conditionally joining; remove the dir/Join logic (or keep a minimal
defensive comment if you prefer) so the function returns cdFilename when
cdFilename != "" and !userSpecifiedOutput.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 76bdd1b6-a6b4-442b-b8d0-70ab3bde606c

📥 Commits

Reviewing files that changed from the base of the PR and between 94bba91 and efdd9b8.

📒 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

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 22, 2026

Codecov Report

❌ Patch coverage is 81.39535% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.96%. Comparing base (3f4352d) to head (cfec997).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/im/im_messages_resources_download.go 81.39% 6 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #536      +/-   ##
==========================================
+ Coverage   59.94%   59.96%   +0.02%     
==========================================
  Files         405      405              
  Lines       42673    42710      +37     
==========================================
+ Hits        25580    25611      +31     
- Misses      15084    15088       +4     
- Partials     2009     2011       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 22, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@cfec99743178a3c710498acadd7c1862e1e0703c

🧩 Skill update

npx skills add chenxingtong-bytedance/cli#fix/filename_adapt -y -g

@chenxingtong-bytedance chenxingtong-bytedance force-pushed the fix/filename_adapt branch 2 times, most recently from f51a3f8 to 1931de5 Compare April 22, 2026 11:26
…esources

Change-Id: I68b48cf428aa8aded4ad9d55fa042f9d68263c3a
@YangJunzhou-01 YangJunzhou-01 merged commit 4da6d61 into larksuite:main Apr 22, 2026
19 checks passed
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.

3 participants