Skip to content

feat(drive): switch markdown export to V2 docs_ai fetch API#948

Merged
fangshuyu-768 merged 1 commit into
mainfrom
feat/drive-export-markdown-v2
May 19, 2026
Merged

feat(drive): switch markdown export to V2 docs_ai fetch API#948
fangshuyu-768 merged 1 commit into
mainfrom
feat/drive-export-markdown-v2

Conversation

@fangshuyu-768

@fangshuyu-768 fangshuyu-768 commented May 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

drive +export --file-extension markdown previously used the V1 content API (GET /open-apis/docs/v1/content) which returns HTML-oriented markdown with poor readability:

  • Tables rendered as raw <table> HTML tags (not standard Markdown | | | syntax)
  • Excessive character escaping (\-, \., \+, \&\#34;)
  • Lost callout emoji and metadata (<div class="callout"> instead of <callout emoji="💡">)
  • Missing whiteboard references (completely dropped)
  • Loose list spacing (~2x file size vs V2)

Switch to the V2 docs_ai fetch API (POST /open-apis/docs_ai/v1/documents/{token}/fetch with format=markdown) which produces clean Lark-flavored Markdown with standard table syntax, preserved semantic tags, and compact formatting.

The file-writing logic (output dir, filename resolution via FetchDriveMetaTitle, overwrite policy) is unchanged.

Changes

  • shortcuts/drive/drive_export.go: DryRun and Execute paths switch from V1 content API to V2 docs_ai fetch API; add docx:document:readonly scope
  • shortcuts/drive/drive_export_test.go: Update 4 test cases to mock the new V2 endpoint

Test Plan

  • go test -race -count=1 -run "TestDriveExport|TestSaveContent|TestValidateDriveExportSpec" ./shortcuts/drive/... — all pass
  • go vet ./shortcuts/drive/... — clean
  • Manual: lark-cli drive +export --token <token> --doc-type docx --file-extension markdown — verify output is Lark-flavored Markdown (standard tables, callout with emoji, no excessive escaping)

Summary by CodeRabbit

  • New Features

    • Added an --api-version flag (default v2) to choose between legacy (v1) and new Lark-flavored (v2) markdown export flows.
  • Bug Fixes

    • Ensure exported Markdown file is written correctly and size_bytes metadata reflects actual content.
  • Documentation

    • Clarified export examples to note Lark-flavored Markdown, api-version behavior, and markdown-only support for docx.
  • Tests

    • Expanded tests to cover v1 and v2 export behaviors and updated response-shape assertions.
  • Chores

    • Added document-read scope required for exports.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Switches drive markdown export to support --api-version (v1|v2, default v2), adds docx:document:readonly scope, implements v2 POST /open-apis/docs_ai/v1/documents/{token}/fetch reading markdown from data.document.content, updates dry-run behavior, tests, and docs.

Changes

Drive Export Markdown Endpoint Migration

Layer / File(s) Summary
Import, scopes, and CLI flag
shortcuts/drive/drive_export.go
Adds validate import for path encoding, extends OAuth scopes with docx:document:readonly, and introduces --api-version (`v1
DryRun: markdown api-version branching
shortcuts/drive/drive_export.go
DryRun now emits legacy GET /open-apis/docs/v1/content for v1 and POST /open-apis/docs_ai/v1/documents/{encoded_token}/fetch with {"format":"markdown"} for v2.
Execute: markdown fetch and content handling
shortcuts/drive/drive_export.go
Execute logs deprecation for v1 legacy GET, uses POST fetch for v2, reads markdown from data.document.content, writes file from []byte(content), and sets size_bytes = len(content).
Tests: update stubs and add v1 test
shortcuts/drive/drive_export_test.go
Stubs updated to POST /open-apis/docs_ai/v1/documents/{token}/fetch returning data.document.content; dry-run assertions include both markdown v2 (docs_ai URL) and markdown v1 (legacy URL); added TestDriveExportMarkdownV1UsesOldAPI.
Docs: --api-version example annotation
skills/lark-drive/references/lark-drive-export.md
Marks exported markdown example as Lark-flavored Markdown and retains note that markdown export only supports docx.

Sequence Diagram

sequenceDiagram
  participant DryRun
  participant Execute
  participant DocsAI as DocsAI Endpoint
  participant File as Output File

  DryRun->>DocsAI: POST /open-apis/docs_ai/v1/documents/{encoded_token}/fetch {"format":"markdown"}
  DocsAI-->>DryRun: Dry-run plan/URL

  Execute->>DocsAI: POST /open-apis/docs_ai/v1/documents/{encoded_token}/fetch {"format":"markdown"}
  DocsAI-->>Execute: {data: {document: {content: "..."}}}
  Execute->>File: Write []byte(content)
  Execute->>File: Record size_bytes = len(content)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • larksuite/cli#194: Prior changes to drive export implementation that this PR modifies and extends.
  • larksuite/cli#685: Related edits to drive export/testing and file-naming behavior that intersect with markdown export changes.

Suggested reviewers

  • wittam-01

Poem

🐰 I hopped from GET to shiny POST today,
Tokens encoded, tests now learn the way.
V2 brings markdown, v1 waves its tail —
Content lands in files, sizes tell the tale.
Hooray for fetches and CI's green trail!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% 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 accurately summarizes the main change: switching markdown export to the V2 docs_ai fetch API, which is the core objective of the PR.
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.
Description check ✅ Passed The PR description comprehensively covers the summary, changes, and test plan, matching the repository template structure with all required sections present.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/drive-export-markdown-v2

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.

@github-actions github-actions Bot added domain/ccm PR touches the ccm domain size/L Large or sensitive change across domains or core paths labels May 18, 2026
@fangshuyu-768
fangshuyu-768 force-pushed the feat/drive-export-markdown-v2 branch from 5bb77c0 to fec448c Compare May 18, 2026 12:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
shortcuts/drive/drive_export_test.go (1)

84-95: ⚡ Quick win

Assert the markdown fetch request body includes {"format":"markdown"}.

These tests currently verify URL/method but not the POST payload contract. Capturing and asserting CapturedBody would catch regressions in the new docs_ai request shape.

Suggested pattern (apply to markdown fetch tests)
-	reg.Register(&httpmock.Stub{
+	fetchStub := &httpmock.Stub{
 		Method: "POST",
 		URL:    "/open-apis/docs_ai/v1/documents/docx123/fetch",
 		Body: map[string]interface{}{
 			"code": 0,
 			"data": map[string]interface{}{
 				"document": map[string]interface{}{
 					"content": "# hello\n",
 				},
 			},
 		},
-	})
+	}
+	reg.Register(fetchStub)
@@
 	if err != nil {
 		t.Fatalf("unexpected error: %v", err)
 	}
+	var req map[string]interface{}
+	if err := json.Unmarshal(fetchStub.CapturedBody, &req); err != nil {
+		t.Fatalf("unmarshal docs_ai fetch body: %v", err)
+	}
+	if req["format"] != "markdown" {
+		t.Fatalf("docs_ai fetch body format = %v, want %q", req["format"], "markdown")
+	}

Also applies to: 137-148, 240-251

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/drive/drive_export_test.go` around lines 84 - 95, Update the
httpmock registration in drive_export_test.go so the POST stub for
"/open-apis/docs_ai/v1/documents/docx123/fetch" captures and asserts the request
body contains {"format":"markdown"}: modify the httpmock.Stub registered via
reg.Register to include a CapturedBody (or use Stub’s capture mechanism) and add
an assertion in the test that the captured body has format == "markdown"; apply
the same change to the other markdown fetch tests referenced (the stubs around
the blocks at the other two locations mentioned) so all markdown fetch tests
validate the POST payload shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shortcuts/drive/drive_export.go`:
- Around line 122-126: The parsing currently silently allows content to be empty
by doing content, _ = doc["content"].(string); change this to fail fast when
document or document.content is missing or not a string: check that
data["document"] is a map[string]interface{} and that doc["content"] exists and
is a non-empty string, and if not return/propagate a clear error (or log and
abort export) instead of proceeding to write an empty file; update the parsing
block that references data, document, and content to validate types and surface
an error upstream so callers of the export routine know the response shape was
invalid.

---

Nitpick comments:
In `@shortcuts/drive/drive_export_test.go`:
- Around line 84-95: Update the httpmock registration in drive_export_test.go so
the POST stub for "/open-apis/docs_ai/v1/documents/docx123/fetch" captures and
asserts the request body contains {"format":"markdown"}: modify the
httpmock.Stub registered via reg.Register to include a CapturedBody (or use
Stub’s capture mechanism) and add an assertion in the test that the captured
body has format == "markdown"; apply the same change to the other markdown fetch
tests referenced (the stubs around the blocks at the other two locations
mentioned) so all markdown fetch tests validate the POST payload shape.
🪄 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: e4a4a6ea-1fd1-4438-a402-138f27f0d7f8

📥 Commits

Reviewing files that changed from the base of the PR and between 7af616b and 5bb77c0.

📒 Files selected for processing (11)
  • shortcuts/common/drive_meta.go
  • shortcuts/common/resource_url.go
  • shortcuts/common/resource_url_test.go
  • shortcuts/drive/drive_export.go
  • shortcuts/drive/drive_export_common.go
  • shortcuts/drive/drive_export_test.go
  • shortcuts/drive/drive_info.go
  • shortcuts/drive/shortcuts.go
  • shortcuts/drive/shortcuts_test.go
  • shortcuts/sheets/lark_sheets_cell_style_and_merge.go
  • tests/cli_e2e/drive/drive_info_dryrun_test.go
💤 Files with no reviewable changes (1)
  • shortcuts/drive/drive_export_common.go

Comment thread shortcuts/drive/drive_export.go
@github-actions

github-actions Bot commented May 18, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#feat/drive-export-markdown-v2 -y -g

@codecov

codecov Bot commented May 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.39%. Comparing base (7af616b) to head (0aa7f32).
⚠️ Report is 17 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #948      +/-   ##
==========================================
+ Coverage   66.71%   67.39%   +0.68%     
==========================================
  Files         563      572       +9     
  Lines       52287    53659    +1372     
==========================================
+ Hits        34884    36165    +1281     
+ Misses      14509    14486      -23     
- Partials     2894     3008     +114     

☔ 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.

@fangshuyu-768
fangshuyu-768 force-pushed the feat/drive-export-markdown-v2 branch from fec448c to efbd29c Compare May 18, 2026 12:27
@github-actions github-actions Bot added size/M Single-domain feat or fix with limited business impact and removed size/L Large or sensitive change across domains or core paths labels May 18, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
shortcuts/drive/drive_export.go (1)

122-126: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast when document.content is missing or invalid.

Line 122–126 silently accepts malformed responses and can write an empty file while reporting success. Return an API error if document or document.content is absent/not a string.

Suggested fix
-			// Extract content from the V2 response: data.document.content
-			var content string
-			if doc, ok := data["document"].(map[string]interface{}); ok {
-				content, _ = doc["content"].(string)
-			}
+			// Extract content from the V2 response: data.document.content
+			doc, ok := data["document"].(map[string]interface{})
+			if !ok {
+				return output.Errorf(output.ExitAPI, "api_error", "invalid markdown fetch response: missing document object")
+			}
+			content, ok := doc["content"].(string)
+			if !ok {
+				return output.Errorf(output.ExitAPI, "api_error", "invalid markdown fetch response: missing document.content")
+			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/drive/drive_export.go` around lines 122 - 126, The code currently
quietly accepts a missing or non-string document.content (the block that sets
var content from data["document"]) which can result in writing an empty file;
update the handler in drive_export.go to validate presence and type of
data["document"] and doc["content"] after the cast, and if either is missing or
not a string return a clear API error (e.g., HTTP 500/400 via the existing
error-return path) instead of proceeding—refer to the variables data, doc and
content in the current extraction block and use the handler's existing error
response mechanism to fail fast.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@shortcuts/drive/drive_export.go`:
- Around line 122-126: The code currently quietly accepts a missing or
non-string document.content (the block that sets var content from
data["document"]) which can result in writing an empty file; update the handler
in drive_export.go to validate presence and type of data["document"] and
doc["content"] after the cast, and if either is missing or not a string return a
clear API error (e.g., HTTP 500/400 via the existing error-return path) instead
of proceeding—refer to the variables data, doc and content in the current
extraction block and use the handler's existing error response mechanism to fail
fast.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7fc28197-5276-4498-ad81-640c08b153a7

📥 Commits

Reviewing files that changed from the base of the PR and between fec448c and efbd29c.

📒 Files selected for processing (2)
  • shortcuts/drive/drive_export.go
  • shortcuts/drive/drive_export_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • shortcuts/drive/drive_export_test.go

@github-actions github-actions Bot added size/L Large or sensitive change across domains or core paths and removed size/M Single-domain feat or fix with limited business impact labels May 18, 2026
@fangshuyu-768
fangshuyu-768 force-pushed the feat/drive-export-markdown-v2 branch from fd561a6 to 31cee95 Compare May 18, 2026 12:42
SunPeiYang996
SunPeiYang996 previously approved these changes May 19, 2026
Switch `drive +export --file-extension markdown` from the legacy V1
GET /open-apis/docs/v1/content API to the V2
POST /open-apis/docs_ai/v1/documents/{token}/fetch API for
higher-quality Lark-flavored Markdown output.

- Update DryRun and Execute paths to use V2 endpoint with JSON body
- Add docx:document:readonly scope for the new API
- Validate V2 response structure (fail fast on missing document/content)
- Encode token in URL path via validate.EncodePathSegment
- Update unit tests and add V2 response validation error path tests
- Add E2E dry-run test for markdown export path
- Update skill documentation
@fangshuyu-768
fangshuyu-768 force-pushed the feat/drive-export-markdown-v2 branch from 04163cb to 0aa7f32 Compare May 19, 2026 08:20
@fangshuyu-768
fangshuyu-768 merged commit 7c54f9b into main May 19, 2026
22 checks passed
@fangshuyu-768
fangshuyu-768 deleted the feat/drive-export-markdown-v2 branch May 19, 2026 09:53
@liangshuo-1 liangshuo-1 mentioned this pull request May 19, 2026
2 tasks
tuxedomm pushed a commit to zhumiaoxin/cli that referenced this pull request Jun 6, 2026
…e#948)

Switch `drive +export --file-extension markdown` from the legacy V1
GET /open-apis/docs/v1/content API to the V2
POST /open-apis/docs_ai/v1/documents/{token}/fetch API for
higher-quality Lark-flavored Markdown output.

- Update DryRun and Execute paths to use V2 endpoint with JSON body
- Add docx:document:readonly scope for the new API
- Validate V2 response structure (fail fast on missing document/content)
- Encode token in URL path via validate.EncodePathSegment
- Update unit tests and add V2 response validation error path tests
- Add E2E dry-run test for markdown export path
- Update skill documentation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/ccm PR touches the ccm domain size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants