Skip to content

fix(docs): resolve wiki links before fetch - #1572

Closed
sunzhipenghit wants to merge 2 commits into
larksuite:mainfrom
sunzhipenghit:fix/issue-1034-wiki-doc-routing-v2
Closed

fix(docs): resolve wiki links before fetch#1572
sunzhipenghit wants to merge 2 commits into
larksuite:mainfrom
sunzhipenghit:fix/issue-1034-wiki-doc-routing-v2

Conversation

@sunzhipenghit

@sunzhipenghit sunzhipenghit commented Jun 25, 2026

Copy link
Copy Markdown

业务背景

Fixes #1034.

When users pass a Wiki URL to docs +fetch, the CLI should not treat the wiki node token as a document token. It must resolve the Wiki node first, then fetch the underlying doc / docx object token. Prompt-only guidance is not stable enough for this behavior.

实现要点

  • Added Wiki node resolution for docs +fetch execution via GET /open-apis/wiki/v2/spaces/get_node.
  • Added dry-run output for the two-step flow: resolve Wiki node, then fetch the resolved document token.
  • Preserved existing doc/docx URL and raw token behavior.
  • Added typed validation errors for non-document Wiki nodes with actionable hints.
  • Added tests for dry-run, resolved docx fetch, and non-document rejection.

降级与局限

无。本 PR only changes docs +fetch; it does not implement cross-shortcut dispatch for Sheets/Base/Slides.

验证

  • go test ./shortcuts/doc -run 'TestDocsFetchWiki|TestDocsFetchDryRunWiki'
  • go test ./shortcuts/doc
  • git diff --check origin/main...HEAD
  • make fmt-check
  • make vet
  • GOFLAGS=-buildvcs=false make unit-test

Note: plain make unit-test failed in this RFC 209 worktree because go list could not obtain VCS status in internal/qualitygate/deptest and suggested -buildvcs=false; the identical target passed with GOFLAGS=-buildvcs=false.

AI Review Context
  • req_id: issue-1034
  • spec_hash: ecd83f429f36744bd8734710aa90b10e35ea1c93f974288da42551032cb7201e
  • spec_path: docs/specs/issue-1034/spec.md
  • subtasks: BE-1
  • acceptance_scenarios:
    • BE-1-AC-1: Wiki URL dry-run shows wiki/v2/spaces/get_node before docs fetch and does not use the raw wiki token as document_id.
    • BE-1-AC-2: obj_type=docx resolves to obj_token and fetches that document.
    • BE-1-AC-3: non-document Wiki nodes return typed validation error with --doc param and actionable hint.
  • scope: shortcuts/doc
  • idl_branch: N/A
  • security_knowledge_ref: UNCONFIGURED
  • security_design_summary: Adds read-only Wiki node resolution before document fetch; no write operation or server contract change.

Summary by CodeRabbit

  • New Features
    • Wiki-based document fetch now performs an initial wiki-node resolution step, then fetches the resolved document content.
  • Bug Fixes
    • Fetch commands now use the resolved document identifier (instead of the raw wiki token) and URL-encode it correctly.
    • Dry-run JSON output no longer HTML-escapes angle brackets, making previews easier to read.

Spec source: active@ecd83f429f36744bd8734710aa90b10e35ea1c93f974288da42551032cb7201e
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Wiki document references now resolve through the wiki node API before fetches run. Dry-run output and tests were updated to cover the extra resolve step, resolved document IDs, and unescaped JSON output.

Changes

Wiki document fetch and dry-run JSON output

Layer / File(s) Summary
Wiki resolution and fetch execution
shortcuts/doc/helpers.go, shortcuts/doc/docs_fetch_v2.go, shortcuts/doc/docs_fetch_v2_test.go
resolveDocumentID resolves doc/docx refs directly and wiki refs through get_node; fetch execution uses the resolved ID, and tests cover wiki fetch success plus invalid resolved node types.
Dry-run JSON rendering
shortcuts/doc/doc_media_test.go, internal/cmdutil/dryrun.go, internal/cmdutil/dryrun_test.go, internal/output/print.go, shortcuts/common/runner.go
Dry-run JSON output uses a no-HTML-escape encoder path, and tests assert the rendered JSON preserves < and > while decoding the API method field.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • larksuite/cli#334: Also extends shortcuts/doc/doc_media_test.go to decode additional dry-run API response fields for assertions.
  • larksuite/cli#1466: Also changes shortcuts/doc/docs_fetch_v2.go fetch execution behavior and request construction.

Suggested reviewers

  • fangshuyu-768

Poem

A bunny found the wiki key,
Then fetched the doc in hop-and-three.
No angle brackets turned to mist,
Just tidy JSON, test-approved list. 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% 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 is concise and accurately describes the main change: resolving wiki links before fetching documents.
Description check ✅ Passed The description covers motivation, implementation, validation, and the related issue, matching the template content even with localized headings.
Linked Issues check ✅ Passed The PR implements the linked wiki-resolution fix for docs +fetch, including dry-run, resolved fetch, and validation for unsupported wiki nodes.
Out of Scope Changes check ✅ Passed All changes support the wiki-resolution fetch flow or its dry-run output; no clearly unrelated code changes stand out.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch fix/issue-1034-wiki-doc-routing-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.

@github-actions github-actions Bot added domain/ccm PR touches the ccm domain size/M Single-domain feat or fix with limited business impact labels Jun 25, 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.

🧹 Nitpick comments (2)
shortcuts/doc/docs_fetch_v2_test.go (1)

177-177: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant LARKSUITE_CLI_CONFIG_DIR setup with TestFactory.

This test builds the CLI via cmdutil.TestFactory, which supplies an in-memory config closure and does not read from the filesystem, so the t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) line is unnecessary here.

Based on learnings: only set LARKSUITE_CLI_CONFIG_DIR when the test exercises the real NewDefault() factory path; shortcut tests using cmdutil.TestFactory(t, config) should not set it.

🤖 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/doc/docs_fetch_v2_test.go` at line 177, Remove the redundant
LARKSUITE_CLI_CONFIG_DIR setup from this shortcut test, since TestFactory
already uses the in-memory config path and does not touch the filesystem. Keep
the test focused on the cmdutil.TestFactory-based flow in docs_fetch_v2_test.go
and only use LARKSUITE_CLI_CONFIG_DIR in tests that go through the real
NewDefault factory.

Source: Learnings

shortcuts/doc/docs_fetch_v2.go (1)

80-80: 📐 Maintainability & Code Quality | 🔵 Trivial

Encode documentID for defensive path construction.

Consistent with shortcuts/drive/drive_export.go (lines 62, 115), use validate.EncodePathSegment when interpolating identifiers into URL paths to prevent injection risks and handle special characters, even if the ID source appears vetted.

Code change
	apiPath := fmt.Sprintf("/open-apis/docs_ai/v1/documents/%s/fetch", documentID)

Should be:

	apiPath := fmt.Sprintf("/open-apis/docs_ai/v1/documents/%s/fetch", validate.EncodePathSegment(documentID))
🤖 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/doc/docs_fetch_v2.go` at line 80, The docs fetch path is building a
URL with an unencoded document identifier, which should be hardened. Update the
`docsFetchV2` path construction to encode `documentID` with
`validate.EncodePathSegment` before interpolating it into the
`/open-apis/docs_ai/v1/documents/.../fetch` route, following the same defensive
pattern used in `drive_export.go` and preserving the existing `apiPath` logic.
🤖 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.

Nitpick comments:
In `@shortcuts/doc/docs_fetch_v2_test.go`:
- Line 177: Remove the redundant LARKSUITE_CLI_CONFIG_DIR setup from this
shortcut test, since TestFactory already uses the in-memory config path and does
not touch the filesystem. Keep the test focused on the cmdutil.TestFactory-based
flow in docs_fetch_v2_test.go and only use LARKSUITE_CLI_CONFIG_DIR in tests
that go through the real NewDefault factory.

In `@shortcuts/doc/docs_fetch_v2.go`:
- Line 80: The docs fetch path is building a URL with an unencoded document
identifier, which should be hardened. Update the `docsFetchV2` path construction
to encode `documentID` with `validate.EncodePathSegment` before interpolating it
into the `/open-apis/docs_ai/v1/documents/.../fetch` route, following the same
defensive pattern used in `drive_export.go` and preserving the existing
`apiPath` logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e660fd6-d9d5-400a-9b10-bf782bab8b42

📥 Commits

Reviewing files that changed from the base of the PR and between 644c3c7 and 943f3ba.

📒 Files selected for processing (4)
  • shortcuts/doc/doc_media_test.go
  • shortcuts/doc/docs_fetch_v2.go
  • shortcuts/doc/docs_fetch_v2_test.go
  • shortcuts/doc/helpers.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 Jun 26, 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.

🧹 Nitpick comments (1)
internal/cmdutil/dryrun.go (1)

130-141: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Encoder logic duplicates output.PrintJsonNoHTMLEscape.

marshalJSONNoHTMLEscape here and the encoder block in internal/output/print.go (PrintJsonNoHTMLEscape) implement the same json.Encoder + SetEscapeHTML(false) + trailing-newline handling. Since this package already imports output, consider exposing a single output-level byte helper and reusing it to keep the no-escape behavior centralized.

Note: the two-layer design is correct here — DryRunAPI.MarshalJSON returns unescaped bytes and the outer PrintJsonNoHTMLEscape also disables escaping, so the compaction step does not re-escape </>. Both must stay in sync, which is an additional reason to share one helper.

🤖 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 `@internal/cmdutil/dryrun.go` around lines 130 - 141, The no-HTML-escape JSON
encoding logic is duplicated between marshalJSONNoHTMLEscape and
output.PrintJsonNoHTMLEscape, so centralize it in one shared output-level byte
helper and reuse it from both places. Update the existing helper in
internal/output/print.go (or add a reusable byte-returning helper there) to
handle json.Encoder, SetEscapeHTML(false), and trimming the trailing newline,
then have DryRunAPI.MarshalJSON call that shared helper instead of maintaining
its own copy. Ensure the no-escape behavior stays identical in both paths.
🤖 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.

Nitpick comments:
In `@internal/cmdutil/dryrun.go`:
- Around line 130-141: The no-HTML-escape JSON encoding logic is duplicated
between marshalJSONNoHTMLEscape and output.PrintJsonNoHTMLEscape, so centralize
it in one shared output-level byte helper and reuse it from both places. Update
the existing helper in internal/output/print.go (or add a reusable
byte-returning helper there) to handle json.Encoder, SetEscapeHTML(false), and
trimming the trailing newline, then have DryRunAPI.MarshalJSON call that shared
helper instead of maintaining its own copy. Ensure the no-escape behavior stays
identical in both paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 75de0993-47e7-4694-b2a9-72ab8ee67180

📥 Commits

Reviewing files that changed from the base of the PR and between 943f3ba and d93083e.

📒 Files selected for processing (6)
  • internal/cmdutil/dryrun.go
  • internal/cmdutil/dryrun_test.go
  • internal/output/print.go
  • shortcuts/common/runner.go
  • shortcuts/doc/docs_fetch_v2.go
  • shortcuts/doc/docs_fetch_v2_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • shortcuts/doc/docs_fetch_v2.go
  • shortcuts/doc/docs_fetch_v2_test.go

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.

非知识库但是文档或在线表格的链接带wiki无法获取

3 participants