Skip to content

feat(apps): add +user-id-convert shortcut for Miaoda↔Feishu ID conversion - #2270

Merged
zhmushan merged 6 commits into
mainfrom
feat/apps-user-id-convert
Aug 11, 2026
Merged

feat(apps): add +user-id-convert shortcut for Miaoda↔Feishu ID conversion#2270
zhmushan merged 6 commits into
mainfrom
feat/apps-user-id-convert

Conversation

@zhmushan

@zhmushan zhmushan commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a read-only apps +user-id-convert shortcut that wraps the existing platform OpenAPI POST /open-apis/spark/v1/directory/user/id_convert, mapping Miaoda user_id ↔ Feishu open-platform IDs (open_id / union_id / Feishu user_id). This unblocks sandbox Code Agents that resolve a person to an open_id via contact/im but need a Miaoda/Feishu user_id for downstream calls (e.g. feishu-approval createApprovalInstance). lark-cli single-repo change, no internal-repo edits.

Changes

  • New shortcut shortcuts/apps/apps_user_id_convert.go — one job, conversion only: no local mapping table, no caching, no permission pre-check, no direction guessing.
    • --convert-type enum → server id_convert_type: miaoda-to-open-id=10, miaoda-to-union-id=11, open-id-to-miaoda=20, union-id-to-miaoda=21, miaoda-to-feishu-user-id=40. Missing/invalid → typed validation error listing allowed directions.
    • --ids — csv / @file / stdin; 1–100 per call (CLI tightens the OpenAPI cap to reject no-op empty batches); not de-duplicated, returned in input order.
    • --dry-run prints the assembled request body without calling; --as user|bot.
    • Server silently drops unresolved IDs, so the CLI reconstructs data.missed by diffing input positions against returned source_ids (reason: not_found), keeping the 0-based input index so callers can back-fill by position even with duplicate IDs.
  • internal/output/envelope.go — adds Total / HitCount / MissedCount as *int on Meta. Pointers so an unset counter is dropped by omitempty while an explicit missed_count: 0 (full hit) is still emitted; non-batch commands leave them nil.
  • Registration — wired into shortcuts/apps/shortcuts.go, updated the count guard + intent index in shortcuts_test.go / skills/lark-apps/SKILL.md.
  • Docs — new skills/lark-apps/references/lark-apps-user-id-convert.md.

Required scope: spark:directory.user.id_convert:read. Rate limit 50 req/s (CLI does not auto-retry).

Test Plan

  • go build ./..., go vet ./shortcuts/apps/... ./internal/output/..., gofmt — all clean
  • go test ./shortcuts/apps/... ./internal/output/... — pass (incl. skill-consistency guard)
  • New tests cover the 4 acceptance cases + full-hit-zero-missed, duplicate-ID-by-position alignment, whole-batch rejection (passthrough code + log_id, no retry), and registration
  • --dry-run verified: miaoda-to-feishu-user-idid_convert_type: 40, endpoint + ids order correct
  • Not verified locally: live end-to-end call with a real authorized identity (requires an app granted spark:directory.user.id_convert:read). Please confirm id_convert_type: 40 is the active value for the Feishu-user-id direction on the live OpenAPI during review.

Note: the pre-existing cmd test TestFrameworkOwnedRootHelpTargetsExistInDefaultTree (mail/user_mailbox…) fails on clean origin/main too — unrelated to this change.

Related Issues

  • Tracked in the Mew issue (SSOT), not a GitHub issue. Source PRD: lark-cli apps ID 转换产品设计.

Summary by CodeRabbit

  • New Features

    • Added a shortcut for converting batches of user IDs between Miaoda and Feishu formats.
    • Supports multiple conversion directions, dry-run previews, authentication options, and up to 100 IDs per request.
    • Accepts IDs from direct input, files, or standard input.
    • Preserves duplicate inputs and reports unresolved IDs.
    • Added usage guidance, examples, and permission details for the conversion command.
  • Bug Fixes

    • Batch result metadata now accurately preserves total, successful, and missed counts, including explicit zero values.

…sion

Wrap the platform id_convert OpenAPI as a read-only shortcut that maps
Miaoda user_id ↔ Feishu open platform IDs (open_id / union_id / Feishu
user_id). It does one thing — conversion — with no local mapping table,
caching, permission pre-check, or direction guessing.

- --convert-type enum → server id_convert_type (10/11/20/21/40)
- --ids: csv / @file / stdin, 1-100 per call, not de-duped, input order
- reconstructs data.missed by diffing input positions against returned
  source_ids (server silently drops unresolved IDs), keyed by 0-based index
- meta counters (total/hit_count/missed_count) via pointer fields on
  output.Meta so an explicit missed_count: 0 survives omitempty
@zhmushan
zhmushan requested a review from liangshuo-1 as a code owner August 10, 2026 11:48
@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

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

The PR adds the +user-id-convert shortcut for batch Miaoda and Feishu user-ID conversion. It adds validation, dry-run support, API requests, positional result reconstruction, metadata counters, loose numeric extraction, tests, registration, and documentation.

Changes

User ID conversion

Layer / File(s) Summary
Conversion flow
internal/output/envelope.go, shortcuts/apps/apps_user_id_convert.go
Adds conversion types, request validation, dry-run output, API execution, duplicate-preserving result reconstruction, missed-ID reporting, and optional result counters.
Conversion behavior validation
shortcuts/apps/apps_user_id_convert_test.go
Tests full and partial conversions, duplicates, input parsing, validation errors, dry-run requests, API failures, numeric JSON IDs, metadata, and registration.
Loose value extraction
shortcuts/common/extract.go, shortcuts/common/extract_test.go
Adds GetStringLoose for nested string, integer, floating-point, and json.Number values. Tests cover precise numeric conversion and missing values.
Shortcut wiring and reference
shortcuts/apps/shortcuts.go, shortcuts/apps/shortcuts_test.go, skills/lark-apps/SKILL.md, skills/lark-apps/references/lark-apps-user-id-convert.md
Registers the shortcut, updates the inventory count, and documents routing, inputs, scopes, outputs, and errors.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AppsUserIDConvert
  participant PlatformAPI
  participant Output
  User->>AppsUserIDConvert: Provide conversion type and IDs
  AppsUserIDConvert->>PlatformAPI: Submit conversion request
  PlatformAPI-->>AppsUserIDConvert: Return resolved IDs
  AppsUserIDConvert->>Output: Emit aligned results and metadata
Loading

Possibly related PRs

  • larksuite/cli#2214: Adds and registers a different Lark CLI shortcut with validation, API handling, tests, and skill documentation.

Suggested labels: feature

Suggested reviewers: liangshuo-1

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.68% 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
Description check ✅ Passed The description covers the summary, changes, test plan, and related issues, and clearly records the pending live verification.
Title check ✅ Passed The title clearly and concisely identifies the new shortcut and its Miaoda-to-Feishu ID conversion purpose.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/apps-user-id-convert

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.

@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: 5

🤖 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/apps/apps_user_id_convert_test.go`:
- Around line 211-220: Expand TestResolveConvertType into table-driven cases
covering every entry in idConvertDirections, including mappings 10, 11, 20, 21,
and 40. Assert each conversion type resolves to its expected ID and preserve the
existing empty-input validation assertions.
- Around line 199-205: Update the validation-error assertions in the affected
conversion tests to call errs.ProblemOf and assert CategoryValidation and
SubtypeInvalidArgument. Retain requireConvertValidation for checking
ValidationError.Param, while preserving the existing hint and message
assertions.

In `@shortcuts/apps/apps_user_id_convert.go`:
- Around line 208-213: Replace the untyped idConvertBody payload with a typed
request struct, and define typed response and item structs for
buildConvertResult. Parse the map[string]interface{} response into the typed
response at the API boundary, rejecting malformed shapes with a typed decoding
error before constructing data.missed. Use a single projection function to
convert each typed item into the existing result shape without coercing invalid
values to empty strings or false not_found results.
- Around line 194-200: Update the CSV parsing loop around strings.Split in the
relevant conversion function to reject any empty trimmed element instead of
continuing past it. Return the existing typed validation error with the element
context, while preserving valid IDs and their original order; add coverage for
an interior empty value such as “id-a,,id-b”.
- Line 101: Add direct coverage for parseConvertIDs via the convert command's
`@file` and stdin inputs, using newline-delimited IDs without commas. Assert that
data.ids contains each expected ID separately, ensuring the input content is
split into individual request IDs rather than treated as one block.
🪄 Autofix

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 Plus

Run ID: a10a2807-e81b-4dad-8762-69b825922f8d

📥 Commits

Reviewing files that changed from the base of the PR and between 8a5afa9 and 1f2c834.

📒 Files selected for processing (7)
  • internal/output/envelope.go
  • shortcuts/apps/apps_user_id_convert.go
  • shortcuts/apps/apps_user_id_convert_test.go
  • shortcuts/apps/shortcuts.go
  • shortcuts/apps/shortcuts_test.go
  • skills/lark-apps/SKILL.md
  • skills/lark-apps/references/lark-apps-user-id-convert.md

Comment thread shortcuts/apps/apps_user_id_convert_test.go
Comment thread shortcuts/apps/apps_user_id_convert_test.go
Comment thread shortcuts/apps/apps_user_id_convert.go
Comment thread shortcuts/apps/apps_user_id_convert.go Outdated
Comment thread shortcuts/apps/apps_user_id_convert.go
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#feat/apps-user-id-convert -y -g

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.78689% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.52%. Comparing base (115357d) to head (aa784c3).

Files with missing lines Patch % Lines
shortcuts/apps/apps_user_id_convert.go 81.55% 11 Missing and 8 partials ⚠️
shortcuts/common/extract.go 88.88% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main    #2270    +/-   ##
========================================
  Coverage   76.51%   76.52%            
========================================
  Files        1019     1020     +1     
  Lines      112614   112736   +122     
========================================
+ Hits        86172    86273   +101     
- Misses      19873    19885    +12     
- Partials     6569     6578     +9     

☔ View full report in Codecov by Harness.
📢 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.

- reject empty --ids CSV entries (e.g. "a,,b") with a typed validation
  error instead of silently dropping them, since a dropped entry shifts
  every later result's 0-based index and breaks the position-keyed
  items/missed contract; add an interior-empty-element test
- reuse common.GetSlice / common.GetString for response projection
  (house convention) instead of local asSlice/asString helpers
- requireConvertValidation now asserts CategoryValidation +
  SubtypeInvalidArgument via errs.ProblemOf, keeping ValidationError.Param
- table-drive TestResolveConvertType over all five directions so every
  --convert-type → id_convert_type mapping (10/11/20/21/40) is protected
…tdin

@file and - (stdin) input arrives verbatim from the framework as
one-ID-per-line text, but parseConvertIDs only split on commas, so such a
block was sent as a single malformed request ID. Treat a newline as
equivalent to a comma, tolerating a file's trailing newline while still
rejecting interior empty entries so position-keyed result indices stay
aligned. Add @file and stdin tests asserting the request body's ids are
split into discrete IDs.
Responses decode with json.Number (client.ParseJSONResponse uses
dec.UseNumber()), so a server that emits source_id/target_id as bare
numbers — plausible for the numeric Miaoda user_id form — was silently
coerced to "" by buildConvertResult's strict string assertion: the
source_id got dropped (false not_found) and the target_id blanked
(false success).

Add common.GetStringLoose, which stringifies string/json.Number/int64/
float64 via literal text (large integer IDs keep full precision, never
routed through a lossy float64), and use it for both id reads. Cover it
with a package-level table test plus an end-to-end regression asserting a
numeric-JSON response yields intact, non-blank ids and no false miss.

Also exercise resolveConvertType's non-empty "not a valid direction"
branch directly, since the runner's enum gate preempts it in normal flow.

@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

🤖 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/common/extract_test.go`:
- Around line 46-77: Add regression coverage in TestGetStringLoose for the int
conversion branch and replace the float64(42) case with a value that would
expose exponent notation under an alternate formatter, while expecting
fixed-point output. Keep the existing table structure and other numeric cases
unchanged.
🪄 Autofix

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 Plus

Run ID: c6731ddd-44e3-4a05-9966-f45639ea4b8a

📥 Commits

Reviewing files that changed from the base of the PR and between 78b8e8d and c64cca3.

📒 Files selected for processing (4)
  • shortcuts/apps/apps_user_id_convert.go
  • shortcuts/apps/apps_user_id_convert_test.go
  • shortcuts/common/extract.go
  • shortcuts/common/extract_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • shortcuts/apps/apps_user_id_convert.go
  • shortcuts/apps/apps_user_id_convert_test.go

Comment thread shortcuts/common/extract_test.go
Add an int-branch case (was only covering int64) and swap the float64
fixture from 42 — which no formatter would render in exponent form — to
1e-7, whose fixed-point rendering "0.0000001" fails under the 'g' verb.
This turns the "no scientific notation" case into a real guard for the
'f' verb choice, per CodeRabbit review on c64cca3.
liangshuo-1
liangshuo-1 previously approved these changes Aug 11, 2026
…vert

# Conflicts:
#	shortcuts/apps/shortcuts_test.go
@zhmushan
zhmushan merged commit 4488da0 into main Aug 11, 2026
27 checks passed
@zhmushan
zhmushan deleted the feat/apps-user-id-convert branch August 11, 2026 11:14
@liangshuo-1 liangshuo-1 mentioned this pull request Aug 11, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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