feat: support multipart doc media uploads#294
Conversation
📝 WalkthroughWalkthroughAdds a shared Drive media uploader with single-part ( Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant LocalFile
participant DriveAPI
Client->>LocalFile: stat/open file → determine size
alt size <= single-part limit
Client->>DriveAPI: POST /open-apis/drive/v1/medias/upload_all (multipart/form-data)
DriveAPI-->>Client: { code, data{ file_token } }
else multipart
Client->>DriveAPI: POST /open-apis/drive/v1/medias/upload_prepare (json)
DriveAPI-->>Client: { upload_id, block_size, block_num }
loop seq = 1..block_num
Client->>LocalFile: read block (block_size)
Client->>DriveAPI: POST /open-apis/drive/v1/medias/upload_part (multipart: seq, upload_id, chunk)
DriveAPI-->>Client: { code, data }
end
Client->>DriveAPI: POST /open-apis/drive/v1/medias/upload_finish (json: upload_id, block_num)
DriveAPI-->>Client: { code, data{ file_token } }
end
Client->>LocalFile: close file
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
shortcuts/common/drive_media_upload_test.go (1)
412-426: Consider adding config directory isolation.Per coding guidelines, test config state should be isolated using
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()). While the atomic counter provides AppID uniqueness, adding config directory isolation would ensure complete test isolation.💡 Suggested improvement
func newDriveMediaUploadTestRuntime(t *testing.T) (*RuntimeContext, *httpmock.Registry) { t.Helper() + t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) cfg := &core.CliConfig{ AppID: fmt.Sprintf("common-drive-media-test-%d", commonDriveMediaUploadTestSeq.Add(1)), AppSecret: "test-secret", Brand: core.BrandFeishu, }Based on learnings: "Isolate config state in Go tests by using
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/common/drive_media_upload_test.go` around lines 412 - 426, The test helper newDriveMediaUploadTestRuntime should isolate CLI config state by setting a temporary config dir; call t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) near the top of newDriveMediaUploadTestRuntime (before creating cfg or calling cmdutil.TestFactory) so each test uses its own config directory and avoids shared state.shortcuts/drive/drive_import_common.go (1)
86-95: Consider usingvfs.Statinstead ofos.Statfor filesystem access.Per coding guidelines, filesystem operations should use
vfs.*instead ofos.*. However, since this line existed before this PR (it's not marked with~), this may be pre-existing technical debt rather than a new violation introduced by this change.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/drive/drive_import_common.go` around lines 86 - 95, Replace the direct os.Stat call in uploadMediaForImport with the virtual filesystem helper: call vfs.Stat (matching the project's vfs API) to get importInfo and err, update imports accordingly, and keep the same error handling/flow (i.e., return output.ErrValidation(...) on error and pass importInfo.Size() into validateDriveImportFileSize). Ensure the change is made in the uploadMediaForImport function and that validateDriveImportFileSize continues to be called with the same filePath, docType, and fileSize.
🤖 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/common/drive_media_upload.go`:
- Around line 52-85: The file open calls in UploadDriveMediaAll and
uploadDriveMediaMultipartParts must validate the file paths using
validate.SafeInputPath before calling os.Open; update UploadDriveMediaAll
(around the os.Open(cfg.FilePath) call) and uploadDriveMediaMultipartParts
(around the os.Open usage at the other site) to call
validate.SafeInputPath(cfg.FilePath) (or the equivalent variable) and return a
validation error if it fails, then proceed to os.Open only after the path is
validated to match the project's safe-path pattern.
---
Nitpick comments:
In `@shortcuts/common/drive_media_upload_test.go`:
- Around line 412-426: The test helper newDriveMediaUploadTestRuntime should
isolate CLI config state by setting a temporary config dir; call
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) near the top of
newDriveMediaUploadTestRuntime (before creating cfg or calling
cmdutil.TestFactory) so each test uses its own config directory and avoids
shared state.
In `@shortcuts/drive/drive_import_common.go`:
- Around line 86-95: Replace the direct os.Stat call in uploadMediaForImport
with the virtual filesystem helper: call vfs.Stat (matching the project's vfs
API) to get importInfo and err, update imports accordingly, and keep the same
error handling/flow (i.e., return output.ErrValidation(...) on error and pass
importInfo.Size() into validateDriveImportFileSize). Ensure the change is made
in the uploadMediaForImport function and that validateDriveImportFileSize
continues to be called with the same filePath, docType, and fileSize.
🪄 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: 32e66156-b312-4cb3-b63d-97fcecd1d735
📒 Files selected for processing (8)
shortcuts/common/drive_media_upload.goshortcuts/common/drive_media_upload_test.goshortcuts/doc/doc_media_insert.goshortcuts/doc/doc_media_test.goshortcuts/doc/doc_media_upload.goshortcuts/drive/drive_import_common.goshortcuts/drive/drive_import_common_test.goskills/lark-doc/references/lark-doc-media-insert.md
💤 Files with no reviewable changes (1)
- shortcuts/drive/drive_import_common_test.go
Greptile SummaryThis PR adds multipart upload support for doc media flows, extracts a shared
Confidence Score: 4/5Safe to merge after fixing the buffer sizing panic risk in uploadDriveMediaMultipartParts. One P1 finding: the buffer in uploadDriveMediaMultipartParts is capped to min(blockSize, fileSize) but the loop slice can reach session.BlockSize when remaining==0, causing a panic on a second iteration. This is a real runtime crash path (even if it requires an inconsistent server response), warranting a fix before merge. All other findings are P2 style concerns. shortcuts/common/drive_media_upload.go — the uploadDriveMediaMultipartParts buffer sizing logic (lines 186–194)
|
| Filename | Overview |
|---|---|
| shortcuts/common/drive_media_upload.go | New shared helper for Drive media upload_all and multipart upload; contains a panic-risk in uploadDriveMediaMultipartParts where the buffer is sized to min(blockSize, fileSize) but the loop indexes up to session.BlockSize unconditionally when remaining==0. |
| shortcuts/doc/doc_media_insert.go | Refactored to delegate upload logic to common helpers and support multipart; filePath = safeFilePath was removed, leading to redundant double-validation but no security risk. |
| shortcuts/doc/doc_media_upload.go | Adds uploadDocMediaFile helper and docMediaShouldUseMultipart; auto-selects upload_all vs multipart cleanly based on file size threshold. |
| shortcuts/drive/drive_import_common.go | Refactored to reuse common.UploadDriveMediaAll and common.UploadDriveMediaMultipart; logic is correct and thresholds are appropriately delegated to the common constant. |
| shortcuts/common/drive_media_upload_test.go | Good coverage of upload_all, multipart prepare/part/finish flows, error cases; multipart body decoder helper is still duplicated across packages (flagged in a prior thread). |
| shortcuts/doc/doc_media_test.go | Added tests for multipart dry-run selection in both media-upload and media-insert; covers wiki resolution, file-not-found, and overwrite protection flows. |
| shortcuts/drive/drive_upload.go | Unchanged in logic; still uses the separate files/upload_* endpoint with its own inline multipart code — intentional, as it is a different API surface from medias/upload_*. |
| skills/lark-doc/references/lark-doc-media-insert.md | Documentation updated to reflect automatic multipart upload for files over 20MB; accurate and consistent with code behavior. |
Sequence Diagram
sequenceDiagram
participant CLI
participant common.UploadDriveMedia
participant LarkAPI
CLI->>CLI: stat(filePath)
alt fileSize <= 20MB
CLI->>common.UploadDriveMedia: UploadDriveMediaAll(cfg)
common.UploadDriveMedia->>LarkAPI: POST /drive/v1/medias/upload_all
LarkAPI-->>common.UploadDriveMedia: file_token
common.UploadDriveMedia-->>CLI: file_token
else fileSize > 20MB
CLI->>common.UploadDriveMedia: UploadDriveMediaMultipart(cfg)
common.UploadDriveMedia->>LarkAPI: POST /drive/v1/medias/upload_prepare
LarkAPI-->>common.UploadDriveMedia: upload_id, block_size, block_num
loop for each block (seq 0..block_num-1)
common.UploadDriveMedia->>LarkAPI: POST /drive/v1/medias/upload_part
LarkAPI-->>common.UploadDriveMedia: ok
end
common.UploadDriveMedia->>LarkAPI: POST /drive/v1/medias/upload_finish
LarkAPI-->>common.UploadDriveMedia: file_token
common.UploadDriveMedia-->>CLI: file_token
end
Reviews (5): Last reviewed commit: "support multipart doc media uploads" | Re-trigger Greptile
🚀 PR Preview Install Guide🧰 CLI updatenpm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@04fdb548a340dadde56de2e2b5f4039ecf7d371b🧩 Skill updatenpx skills add larksuite/cli#feat/doc-media-chunked-upload -y -g |
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
shortcuts/common/drive_media_upload.go (1)
52-57:⚠️ Potential issue | 🟠 MajorValidate and preflight the file path inside the shared uploader.
UploadDriveMediaAlland the multipart path still touch raw file paths, and multipart does not surface path/open failures until afterupload_preparehas already created a remote session. Please safe-validate once inside the common helper and pass the sanitized/opened file into the part loop so callers cannot bypass the repo’s path guard. As per coding guidelines,**/*.go: Validate paths usingvalidate.SafeInputPathbefore any file I/O operations.Also applies to: 87-116, 170-175
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/common/drive_media_upload.go` around lines 52 - 57, UploadDriveMediaAll currently opens cfg.FilePath directly; validate and sanitize the path first using validate.SafeInputPath before any file I/O, then open the returned safe path and pass the opened file handle (not the raw path) into the multipart/part loop so path/open failures surface before upload_prepare creates a remote session. Update UploadDriveMediaAll to call validate.SafeInputPath(cfg.FilePath), handle/return validation errors, open the sanitized path, defer close, and refactor the multipart upload code to accept the io.Reader/File (the opened file) instead of re-opening the raw path so callers cannot bypass the repo path guard.
🤖 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/common/drive_media_upload.go`:
- Around line 177-182: session.BlockSize must be bounded by the actual local
file size before allocating buffer to avoid OOM: retrieve the local file size
(e.g., via file.Stat().Size() or existing localFileSize variable), compute
allowed := min(session.BlockSize, localFileSize - currentOffset) and also clamp
to the existing maxInt check, then allocate buffer using make([]byte,
int(allowed)) instead of make([]byte, int(session.BlockSize)); ensure allowed is
>= 1 and fall back to a safe default or return an error if it's invalid.
In `@shortcuts/doc/doc_media_insert.go`:
- Around line 82-83: The dry-run helper appendDocMediaInsertUploadDryRun is
creating an upload preview that doesn't include the same payload fields used in
real execution (uploadDocMediaFile attaches the document "extra" field and the
single-part path includes "size"), so update appendDocMediaInsertUploadDryRun to
include the document extra metadata and the size field in its generated request
body for both multipart and single-part flows so the preview matches what
uploadDocMediaFile actually sends; ensure the same keys/naming are used as in
uploadDocMediaFile and mirror any conditional logic for multipart vs single-part
inserts.
In `@shortcuts/doc/doc_media_upload.go`:
- Around line 43-75: The dry-run for single-part uploads is missing the "size"
field so it doesn't match the real UploadDriveMediaAll request; update the
single-part branch (the code that sets body["file"] = "@" + filePath and returns
dry.Desc("multipart/form-data upload")...) to include the same "size" key as the
real request (set body["size"] to the file size or placeholder "<file_size>" so
the dry run mirrors UploadDriveMediaAll), ensuring the dry-run Body payload keys
match those used when not using docMediaShouldUseMultipart.
---
Duplicate comments:
In `@shortcuts/common/drive_media_upload.go`:
- Around line 52-57: UploadDriveMediaAll currently opens cfg.FilePath directly;
validate and sanitize the path first using validate.SafeInputPath before any
file I/O, then open the returned safe path and pass the opened file handle (not
the raw path) into the multipart/part loop so path/open failures surface before
upload_prepare creates a remote session. Update UploadDriveMediaAll to call
validate.SafeInputPath(cfg.FilePath), handle/return validation errors, open the
sanitized path, defer close, and refactor the multipart upload code to accept
the io.Reader/File (the opened file) instead of re-opening the raw path so
callers cannot bypass the repo path guard.
🪄 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: 3a9326a7-daf4-491f-9402-3df64057b744
📒 Files selected for processing (6)
shortcuts/common/drive_media_upload.goshortcuts/doc/doc_media_insert.goshortcuts/doc/doc_media_test.goshortcuts/doc/doc_media_upload.goshortcuts/drive/drive_import.goshortcuts/drive/drive_import_common.go
💤 Files with no reviewable changes (1)
- shortcuts/drive/drive_import.go
✅ Files skipped from review due to trivial changes (1)
- shortcuts/drive/drive_import_common.go
🚧 Files skipped from review as they are similar to previous changes (1)
- shortcuts/doc/doc_media_test.go
2f072cf to
769ab52
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
shortcuts/drive/drive_upload.go (1)
184-184:⚠️ Potential issue | 🟡 MinorUse
vfs.Openinstead ofos.Openfor filesystem access.The multipart upload loop uses
os.Openwhile other file operations in this file correctly usevfs.Open(line 99) andvfs.Stat(line 70). As per coding guidelines, all filesystem access should usevfs.*for consistency and testability.🔧 Proposed fix
- partFile, err := os.Open(filePath) + partFile, err := vfs.Open(filePath)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/drive/drive_upload.go` at line 184, Replace the direct os.Open call with the VFS wrapper: open the part file using vfs.Open(filePath) instead of os.Open(filePath) (the variable partFile and error handling usage should remain the same), ensure the import set is updated (remove unused "os" if no longer needed), and keep the existing defer/Close logic unchanged so the multipart upload loop continues to use vfs for filesystem access and remains testable; update any error messages referencing os.Open to reflect vfs.Open if present.
🤖 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/drive/drive_upload.go`:
- Line 184: Replace the direct os.Open call with the VFS wrapper: open the part
file using vfs.Open(filePath) instead of os.Open(filePath) (the variable
partFile and error handling usage should remain the same), ensure the import set
is updated (remove unused "os" if no longer needed), and keep the existing
defer/Close logic unchanged so the multipart upload loop continues to use vfs
for filesystem access and remains testable; update any error messages
referencing os.Open to reflect vfs.Open if present.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 518afbed-666a-4a55-9816-32a852516b52
📒 Files selected for processing (16)
shortcuts/common/drive_media_upload.goshortcuts/common/drive_media_upload_test.goshortcuts/doc/doc_media_insert.goshortcuts/doc/doc_media_test.goshortcuts/doc/doc_media_upload.goshortcuts/drive/drive_export_test.goshortcuts/drive/drive_import.goshortcuts/drive/drive_import_common.goshortcuts/drive/drive_import_common_test.goshortcuts/drive/drive_import_test.goshortcuts/drive/drive_io_test.goshortcuts/drive/drive_move_common_test.goshortcuts/drive/drive_move_test.goshortcuts/drive/drive_task_result_test.goshortcuts/drive/drive_upload.goskills/lark-doc/references/lark-doc-media-insert.md
💤 Files with no reviewable changes (5)
- shortcuts/drive/drive_task_result_test.go
- shortcuts/drive/drive_move_test.go
- shortcuts/drive/drive_export_test.go
- shortcuts/drive/drive_import_common_test.go
- shortcuts/drive/drive_move_common_test.go
✅ Files skipped from review due to trivial changes (2)
- shortcuts/drive/drive_import_test.go
- skills/lark-doc/references/lark-doc-media-insert.md
🚧 Files skipped from review as they are similar to previous changes (4)
- shortcuts/common/drive_media_upload_test.go
- shortcuts/doc/doc_media_upload.go
- shortcuts/doc/doc_media_test.go
- shortcuts/common/drive_media_upload.go
769ab52 to
4d4b814
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
shortcuts/doc/doc_media_upload.go (1)
35-75:⚠️ Potential issue | 🟡 MinorAdd
sizefield to single-part dry-run body for consistency.The single-part dry-run (lines 72-75) omits the
sizefield, but the realUploadDriveMediaAllalways sends it. The multipart dry-run correctly includes"size": "<file_size>"at line 49, but the single-part path should match.📝 Proposed fix
body["file"] = "@" + filePath + body["size"] = "<file_size>" return dry.Desc("multipart/form-data upload"). POST("/open-apis/drive/v1/medias/upload_all"). Body(body)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/doc/doc_media_upload.go` around lines 35 - 75, The single-part dry-run body built in this function is missing the "size" field; update the body map (the one with keys "file_name", "parent_type", "parent_node" and optional "extra") to include "size": "<file_size>" before assigning body["file"] and returning the dry run for the multipart/form-data path (the block that returns dry.Desc("multipart/form-data upload").POST("/open-apis/drive/v1/medias/upload_all").Body(body)); this makes the single-part dry-run consistent with the multipart dry-run that already sets "size".
🤖 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/doc/doc_media_upload.go`:
- Around line 35-75: The single-part dry-run body built in this function is
missing the "size" field; update the body map (the one with keys "file_name",
"parent_type", "parent_node" and optional "extra") to include "size":
"<file_size>" before assigning body["file"] and returning the dry run for the
multipart/form-data path (the block that returns dry.Desc("multipart/form-data
upload").POST("/open-apis/drive/v1/medias/upload_all").Body(body)); this makes
the single-part dry-run consistent with the multipart dry-run that already sets
"size".
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2a53f556-fd46-4cea-8878-47aa03afaf9e
📒 Files selected for processing (16)
shortcuts/common/drive_media_upload.goshortcuts/common/drive_media_upload_test.goshortcuts/doc/doc_media_insert.goshortcuts/doc/doc_media_test.goshortcuts/doc/doc_media_upload.goshortcuts/drive/drive_export_test.goshortcuts/drive/drive_import.goshortcuts/drive/drive_import_common.goshortcuts/drive/drive_import_common_test.goshortcuts/drive/drive_import_test.goshortcuts/drive/drive_io_test.goshortcuts/drive/drive_move_common_test.goshortcuts/drive/drive_move_test.goshortcuts/drive/drive_task_result_test.goshortcuts/drive/drive_upload.goskills/lark-doc/references/lark-doc-media-insert.md
💤 Files with no reviewable changes (5)
- shortcuts/drive/drive_move_test.go
- shortcuts/drive/drive_move_common_test.go
- shortcuts/drive/drive_export_test.go
- shortcuts/drive/drive_task_result_test.go
- shortcuts/drive/drive_import_common_test.go
✅ Files skipped from review due to trivial changes (2)
- skills/lark-doc/references/lark-doc-media-insert.md
- shortcuts/drive/drive_upload.go
🚧 Files skipped from review as they are similar to previous changes (5)
- shortcuts/drive/drive_import_test.go
- shortcuts/drive/drive_import.go
- shortcuts/common/drive_media_upload_test.go
- shortcuts/doc/doc_media_insert.go
- shortcuts/doc/doc_media_test.go
4d4b814 to
477e7a5
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
shortcuts/doc/doc_media_test.go (1)
59-79:⚠️ Potential issue | 🟡 MinorUse the standard isolated test setup here.
These updated tests still create factories without isolating
LARKSUITE_CLI_CONFIG_DIR, andwriteSizedDocTestFilegoes throughos.Createinstead of the repo FS abstraction. That keeps the tests on shared config/filesystem behavior instead of the expected harness.As per coding guidelines,
**/*_test.go:Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()); and**/*.go:Use vfs.* instead of os.* for all filesystem accessplusValidate paths using validate.SafeInputPath before any file I/O operations.Also applies to: 297-310
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/doc/doc_media_test.go` around lines 59 - 79, Tests (e.g., TestDocMediaInsertRejectsOldDocURL and the helpers like writeSizedDocTestFile and mountAndRunDocs using docsTestConfigWithAppID) are not isolating config dir or using the repo VFS; update the tests to call t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) at start of each test to isolate config state, replace any os.* usage in writeSizedDocTestFile (and other test helpers) with the repository vfs.* APIs, and validate file paths with validate.SafeInputPath before creating/writing files so all filesystem access is through the virtual FS and paths are safe.
🤖 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/common/drive_media_upload_test.go`:
- Around line 151-177: The prepare-phase assertions are too loose: ensure the
"parent_node" key is present and explicitly equals the empty string (not just
that casting yields ""), and verify the import "extra" payload is preserved into
upload_prepare by asserting prepareBody contains the extra data (e.g., that
prepareBody["extra"] has obj_type == "sheet" and file_extension == "xlsx" or the
exact extra string passed); update the checks after
decodeCapturedDriveMediaJSONBody(t, prepareStub) to explicitly test presence and
values of "parent_node" and the "extra" fields so
UploadDriveMediaMultipart/prepare handling is validated.
- Around line 411-467: newDriveMediaUploadTestRuntime currently mutates shared
config state; call t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) at the top
of newDriveMediaUploadTestRuntime so TestFactory and core.CliConfig use an
isolated config dir. Replace all direct os.* filesystem calls in
withDriveMediaUploadWorkingDir, writeDriveMediaUploadTestFile, and
writeDriveMediaUploadSizedFile with the repo vfs equivalents (e.g.,
vfs.Getwd/vfs.Chdir/vfs.WriteFile/vfs.Create/vfs.Truncate/vfs.Close) and
validate every input path with validate.SafeInputPath before performing I/O.
Ensure t.Cleanup still restores the working dir via vfs and keep the same helper
signatures (withDriveMediaUploadWorkingDir, writeDriveMediaUploadTestFile,
writeDriveMediaUploadSizedFile) and the RuntimeContext creation logic unchanged
aside from the environment isolation.
---
Outside diff comments:
In `@shortcuts/doc/doc_media_test.go`:
- Around line 59-79: Tests (e.g., TestDocMediaInsertRejectsOldDocURL and the
helpers like writeSizedDocTestFile and mountAndRunDocs using
docsTestConfigWithAppID) are not isolating config dir or using the repo VFS;
update the tests to call t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) at
start of each test to isolate config state, replace any os.* usage in
writeSizedDocTestFile (and other test helpers) with the repository vfs.* APIs,
and validate file paths with validate.SafeInputPath before creating/writing
files so all filesystem access is through the virtual FS and paths are safe.
🪄 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: 017c3728-7e34-4c4b-9cfb-30b4fc41d0a3
📒 Files selected for processing (16)
shortcuts/common/drive_media_upload.goshortcuts/common/drive_media_upload_test.goshortcuts/doc/doc_media_insert.goshortcuts/doc/doc_media_test.goshortcuts/doc/doc_media_upload.goshortcuts/drive/drive_export_test.goshortcuts/drive/drive_import.goshortcuts/drive/drive_import_common.goshortcuts/drive/drive_import_common_test.goshortcuts/drive/drive_import_test.goshortcuts/drive/drive_io_test.goshortcuts/drive/drive_move_common_test.goshortcuts/drive/drive_move_test.goshortcuts/drive/drive_task_result_test.goshortcuts/drive/drive_upload.goskills/lark-doc/references/lark-doc-media-insert.md
💤 Files with no reviewable changes (5)
- shortcuts/drive/drive_move_common_test.go
- shortcuts/drive/drive_export_test.go
- shortcuts/drive/drive_import_common_test.go
- shortcuts/drive/drive_move_test.go
- shortcuts/drive/drive_task_result_test.go
✅ Files skipped from review due to trivial changes (2)
- shortcuts/drive/drive_upload.go
- skills/lark-doc/references/lark-doc-media-insert.md
🚧 Files skipped from review as they are similar to previous changes (5)
- shortcuts/drive/drive_import_test.go
- shortcuts/drive/drive_import.go
- shortcuts/drive/drive_io_test.go
- shortcuts/common/drive_media_upload.go
- shortcuts/doc/doc_media_insert.go
There was a problem hiding this comment.
♻️ Duplicate comments (3)
shortcuts/doc/doc_media_insert.go (1)
82-83:⚠️ Potential issue | 🟡 MinorKeep the insert dry-run upload body aligned with execution.
uploadDocMediaFile()always attaches the doc routeextra, but the dry-run builder still omits it from bothupload_prepareandupload_all. The previewed request shape is therefore still incomplete fordocs +media-insert.🧩 Suggested fix
- appendDocMediaInsertUploadDryRun(d, filePath, parentType, stepBase+2) + appendDocMediaInsertUploadDryRun(d, filePath, parentType, documentID, stepBase+2) ... -func appendDocMediaInsertUploadDryRun(d *common.DryRunAPI, filePath, parentType string, step int) { +func appendDocMediaInsertUploadDryRun(d *common.DryRunAPI, filePath, parentType, documentID string, step int) { + extra := fmt.Sprintf(`{"drive_route_token":"%s"}`, documentID) // The upload step runs only after the empty placeholder block is created, so // dry-run can refer to that future block ID only symbolically. For large // files, keep multipart internals as substeps of the single user-facing // "upload file" step. if docMediaShouldUseMultipart(filePath) { d.POST("/open-apis/drive/v1/medias/upload_prepare"). Desc(fmt.Sprintf("[%da] Initialize multipart upload", step)). Body(map[string]interface{}{ "file_name": filepath.Base(filePath), "parent_type": parentType, "parent_node": "<new_block_id>", "size": "<file_size>", + "extra": extra, }). POST("/open-apis/drive/v1/medias/upload_part"). @@ d.POST("/open-apis/drive/v1/medias/upload_all"). Desc(fmt.Sprintf("[%d] Upload local file (multipart/form-data)", step)). Body(map[string]interface{}{ "file_name": filepath.Base(filePath), "parent_type": parentType, "parent_node": "<new_block_id>", "size": "<file_size>", + "extra": extra, "file": "@" + filePath, }) }Also applies to: 350-389
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/doc/doc_media_insert.go` around lines 82 - 83, The dry-run builder in appendDocMediaInsertUploadDryRun is missing the doc route "extra" that uploadDocMediaFile always attaches, so align the previewed request shape by adding the same "extra" doc route to both upload_prepare and upload_all entries in appendDocMediaInsertUploadDryRun (and any other dry-run builders in the 350-389 region); ensure the keys and values match what uploadDocMediaFile attaches so the dry-run payload mirrors execution.shortcuts/common/drive_media_upload_test.go (2)
411-467:⚠️ Potential issue | 🟠 MajorMake the test harness hermetic and use repo-standard file helpers.
This suite still shares the default CLI config dir, and the new helpers write through
os.*. That keeps the tests outside the repo's filesystem/config conventions and can leak machine-local state into the run.🧪 Suggested starting point
func newDriveMediaUploadTestRuntime(t *testing.T) (*RuntimeContext, *httpmock.Registry) { t.Helper() + t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) cfg := &core.CliConfig{As per coding guidelines,
**/*_test.go:Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()); and**/*.go:Use vfs.* instead of os.* for all filesystem accessplusValidate paths using validate.SafeInputPath before any file I/O operations.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/common/drive_media_upload_test.go` around lines 411 - 467, The tests currently touch real filesystem and share the default CLI config dir; update newDriveMediaUploadTestRuntime to call t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) to isolate config state, and modify filesystem helpers (withDriveMediaUploadWorkingDir, writeDriveMediaUploadTestFile, writeDriveMediaUploadSizedFile) to use the repo vfs utilities instead of os.* (change os.Getwd/Chdir/WriteFile/Create/Truncate/Close to the vfs equivalents) and validate all input paths with validate.SafeInputPath before performing any I/O; keep the same function names so callers remain stable.
167-177:⚠️ Potential issue | 🟡 MinorTighten the
upload_prepareassertions.The current
parent_nodecheck still passes when the key is absent, and this path never asserts that the importextrapayload survives intoupload_prepare. That leaves the import-specific contract under-tested.🧪 Suggested assertion update
prepareBody := decodeCapturedDriveMediaJSONBody(t, prepareStub) if got, _ := prepareBody["parent_type"].(string); got != "ccm_import_open" { t.Fatalf("prepare parent_type = %q, want %q", got, "ccm_import_open") } - if got, _ := prepareBody["parent_node"].(string); got != "" { - t.Fatalf("prepare parent_node = %q, want empty string", got) - } + rawParentNode, ok := prepareBody["parent_node"] + if !ok { + t.Fatal("prepare body missing parent_node") + } + if got, ok := rawParentNode.(string); !ok || got != "" { + t.Fatalf("prepare parent_node = %#v, want empty string", rawParentNode) + } + if got, _ := prepareBody["extra"].(string); got != `{"obj_type":"sheet","file_extension":"xlsx"}` { + t.Fatalf("prepare extra = %q, want import payload", got) + } if got, _ := prepareBody["size"].(float64); got != float64(MaxDriveMediaUploadSinglePartSize+1) { t.Fatalf("prepare size = %v, want %d", got, MaxDriveMediaUploadSinglePartSize+1) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/common/drive_media_upload_test.go` around lines 167 - 177, The prepare-body assertions are too loose: ensure the "parent_node" key is present and explicitly equals the empty string (not just treating a missing key as ""), and add an assertion that the import-specific "extra" payload passed into upload_prepare survives into the captured prepareBody; use decodeCapturedDriveMediaJSONBody(t, prepareStub) to read prepareBody and assert prepareBody["parent_node"] exists and is "" and that prepareBody["extra"] (as a map/object) contains the expected import fields/values from the original import payload, keeping the existing size check using MaxDriveMediaUploadSinglePartSize+1.
🧹 Nitpick comments (2)
shortcuts/doc/doc_media_test.go (2)
60-60: Isolate config state in the new factory call sites.These updated
cmdutil.TestFactoryusages still run against the default config dir, so local config/cache can leak into the docs test suite. A small helper aroundTestFactorywould fix this once for the file.As per coding guidelines,
**/*_test.go:Isolate config state in Go tests by using t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()).Also applies to: 78-78
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/doc/doc_media_test.go` at line 60, The TestFactory calls in doc_media_test.go (e.g., the call using cmdutil.TestFactory and helper docsTestConfigWithAppID) are using the default config dir and may leak local config; wrap those calls in a small local helper that first calls t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) and then returns the results of cmdutil.TestFactory(t, docsTestConfigWithAppID("docs-test-app")) (apply the same helper to the other site at the second call around line ~78) so each test gets an isolated config/cache directory before invoking TestFactory.
297-310: Use the repo filesystem helpers in this test utility.This new helper writes directly via
os.Create, which bypasses the same VFS/path-validation path the command code uses.♻️ Suggested change
func writeSizedDocTestFile(t *testing.T, name string, size int64) { t.Helper() - fh, err := os.Create(name) + safeName, err := validate.SafeInputPath(name) + if err != nil { + t.Fatalf("SafeInputPath(%q) error: %v", name, err) + } + fh, err := vfs.Create(safeName) if err != nil { - t.Fatalf("Create(%q) error: %v", name, err) + t.Fatalf("Create(%q) error: %v", safeName, err) }As per coding guidelines,
**/*.go:Use vfs.* instead of os.* for all filesystem accessandValidate paths using validate.SafeInputPath before any file I/O operations.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/doc/doc_media_test.go` around lines 297 - 310, The helper writeSizedDocTestFile currently uses os.Create/Truncate/Close and bypasses the repository VFS and path validation; update it to call validate.SafeInputPath(name) before any I/O and use the repository VFS helpers (e.g. vfs.Create or vfs.OpenFile and then file.Truncate/file.Close or vfs.Truncate) instead of os.* so the test goes through the same VFS/path checks as production code; keep the same error handling via t.Fatalf but replace os.Create, fh.Truncate and fh.Close with their vfs equivalents and validate.SafeInputPath at the top of the function.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@shortcuts/common/drive_media_upload_test.go`:
- Around line 411-467: The tests currently touch real filesystem and share the
default CLI config dir; update newDriveMediaUploadTestRuntime to call
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) to isolate config state, and
modify filesystem helpers (withDriveMediaUploadWorkingDir,
writeDriveMediaUploadTestFile, writeDriveMediaUploadSizedFile) to use the repo
vfs utilities instead of os.* (change
os.Getwd/Chdir/WriteFile/Create/Truncate/Close to the vfs equivalents) and
validate all input paths with validate.SafeInputPath before performing any I/O;
keep the same function names so callers remain stable.
- Around line 167-177: The prepare-body assertions are too loose: ensure the
"parent_node" key is present and explicitly equals the empty string (not just
treating a missing key as ""), and add an assertion that the import-specific
"extra" payload passed into upload_prepare survives into the captured
prepareBody; use decodeCapturedDriveMediaJSONBody(t, prepareStub) to read
prepareBody and assert prepareBody["parent_node"] exists and is "" and that
prepareBody["extra"] (as a map/object) contains the expected import
fields/values from the original import payload, keeping the existing size check
using MaxDriveMediaUploadSinglePartSize+1.
In `@shortcuts/doc/doc_media_insert.go`:
- Around line 82-83: The dry-run builder in appendDocMediaInsertUploadDryRun is
missing the doc route "extra" that uploadDocMediaFile always attaches, so align
the previewed request shape by adding the same "extra" doc route to both
upload_prepare and upload_all entries in appendDocMediaInsertUploadDryRun (and
any other dry-run builders in the 350-389 region); ensure the keys and values
match what uploadDocMediaFile attaches so the dry-run payload mirrors execution.
---
Nitpick comments:
In `@shortcuts/doc/doc_media_test.go`:
- Line 60: The TestFactory calls in doc_media_test.go (e.g., the call using
cmdutil.TestFactory and helper docsTestConfigWithAppID) are using the default
config dir and may leak local config; wrap those calls in a small local helper
that first calls t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) and then
returns the results of cmdutil.TestFactory(t,
docsTestConfigWithAppID("docs-test-app")) (apply the same helper to the other
site at the second call around line ~78) so each test gets an isolated
config/cache directory before invoking TestFactory.
- Around line 297-310: The helper writeSizedDocTestFile currently uses
os.Create/Truncate/Close and bypasses the repository VFS and path validation;
update it to call validate.SafeInputPath(name) before any I/O and use the
repository VFS helpers (e.g. vfs.Create or vfs.OpenFile and then
file.Truncate/file.Close or vfs.Truncate) instead of os.* so the test goes
through the same VFS/path checks as production code; keep the same error
handling via t.Fatalf but replace os.Create, fh.Truncate and fh.Close with their
vfs equivalents and validate.SafeInputPath at the top of the function.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 56149a51-103c-4df1-90e0-f5c17dc2521a
📒 Files selected for processing (16)
shortcuts/common/drive_media_upload.goshortcuts/common/drive_media_upload_test.goshortcuts/doc/doc_media_insert.goshortcuts/doc/doc_media_test.goshortcuts/doc/doc_media_upload.goshortcuts/drive/drive_export_test.goshortcuts/drive/drive_import.goshortcuts/drive/drive_import_common.goshortcuts/drive/drive_import_common_test.goshortcuts/drive/drive_import_test.goshortcuts/drive/drive_io_test.goshortcuts/drive/drive_move_common_test.goshortcuts/drive/drive_move_test.goshortcuts/drive/drive_task_result_test.goshortcuts/drive/drive_upload.goskills/lark-doc/references/lark-doc-media-insert.md
💤 Files with no reviewable changes (5)
- shortcuts/drive/drive_move_test.go
- shortcuts/drive/drive_move_common_test.go
- shortcuts/drive/drive_export_test.go
- shortcuts/drive/drive_task_result_test.go
- shortcuts/drive/drive_import_common_test.go
✅ Files skipped from review due to trivial changes (2)
- skills/lark-doc/references/lark-doc-media-insert.md
- shortcuts/drive/drive_io_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
- shortcuts/drive/drive_import_test.go
- shortcuts/drive/drive_upload.go
- shortcuts/drive/drive_import_common.go
Change-Id: I9d9fb00079dacfc96b5781e12e6ce79945baa2ed
477e7a5 to
04fdb54
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
shortcuts/common/drive_media_upload.go (1)
186-191: Consider capping buffer allocation to file size for small files.While the buffer is bounded by
maxInt, a largeBlockSizereturned by the backend (e.g., 100MB for a 1KB file) would still allocate unnecessarily. This is a defensive improvement rather than a bug, since the actual reads are bounded byremaining.💡 Optional defensive improvement
maxInt := int64(^uint(0) >> 1) bufferSize := session.BlockSize + if fileSize > 0 && bufferSize > fileSize { + bufferSize = fileSize + } if bufferSize <= 0 || bufferSize > maxInt { return output.Errorf(output.ExitAPI, "api_error", "upload prepare failed: invalid block_size returned") }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@shortcuts/common/drive_media_upload.go` around lines 186 - 191, The code allocates buffer sized to session.BlockSize which can be much larger than the actual remaining file bytes; cap the allocation by taking the minimum of session.BlockSize, the remaining bytes to upload (compute remaining = totalSize - uploadedBytes from your upload loop/state), and maxInt before make([]byte, ...). Update the bufferSize calculation (use session.BlockSize, maxInt and remaining) and replace buffer := make([]byte, int(bufferSize)) so small files don't allocate oversized buffers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@shortcuts/common/drive_media_upload.go`:
- Around line 186-191: The code allocates buffer sized to session.BlockSize
which can be much larger than the actual remaining file bytes; cap the
allocation by taking the minimum of session.BlockSize, the remaining bytes to
upload (compute remaining = totalSize - uploadedBytes from your upload
loop/state), and maxInt before make([]byte, ...). Update the bufferSize
calculation (use session.BlockSize, maxInt and remaining) and replace buffer :=
make([]byte, int(bufferSize)) so small files don't allocate oversized buffers.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: afa28e62-9264-41a9-8993-28803635cbf7
📒 Files selected for processing (16)
shortcuts/common/drive_media_upload.goshortcuts/common/drive_media_upload_test.goshortcuts/doc/doc_media_insert.goshortcuts/doc/doc_media_test.goshortcuts/doc/doc_media_upload.goshortcuts/drive/drive_export_test.goshortcuts/drive/drive_import.goshortcuts/drive/drive_import_common.goshortcuts/drive/drive_import_common_test.goshortcuts/drive/drive_import_test.goshortcuts/drive/drive_io_test.goshortcuts/drive/drive_move_common_test.goshortcuts/drive/drive_move_test.goshortcuts/drive/drive_task_result_test.goshortcuts/drive/drive_upload.goskills/lark-doc/references/lark-doc-media-insert.md
💤 Files with no reviewable changes (5)
- shortcuts/drive/drive_export_test.go
- shortcuts/drive/drive_move_test.go
- shortcuts/drive/drive_task_result_test.go
- shortcuts/drive/drive_import_common_test.go
- shortcuts/drive/drive_move_common_test.go
✅ Files skipped from review due to trivial changes (1)
- skills/lark-doc/references/lark-doc-media-insert.md
🚧 Files skipped from review as they are similar to previous changes (6)
- shortcuts/drive/drive_import_test.go
- shortcuts/drive/drive_io_test.go
- shortcuts/drive/drive_upload.go
- shortcuts/doc/doc_media_upload.go
- shortcuts/drive/drive_import_common.go
- shortcuts/doc/doc_media_insert.go
|
Greptile encountered an error while reviewing this PR. Please reach out to support@greptile.com for assistance. |
Summary
Add multipart upload support for doc media flows so
docs +media-uploadanddocs +media-insertcan handle files larger than 20MB. This also extracts the shared Drive media upload logic into a common helper and keeps the user-facingdocs +media- insertdry-run flow expressed as 4/5 high-level steps.Changes
upload_alland multipartupload_prepare -> upload_part -> upload_finishdocs +media-uploadto auto-switch to multipart upload for files larger than 20MBdocs +media-insertto auto-switch to multipart upload, keep rollback behavior, and keep dry-run output user-orientedshortcuts/commonlark-doc-media-insertreference to reflect automatic multipart upload for files larger than 20MBTest Plan
make unit-test)lark-cli docs +media-insertworks as expected for both a normal image upload and a file upload larger than 20MBRelated Issues
Summary by CodeRabbit
New Features
Documentation
Tests
Chores / Refactor