Skip to content

feat(contact +search-user): add --queries multi-name fanout#707

Merged
liangshuo-1 merged 1 commit into
mainfrom
feat/search-user-multi-queries
Apr 29, 2026
Merged

feat(contact +search-user): add --queries multi-name fanout#707
liangshuo-1 merged 1 commit into
mainfrom
feat/search-user-multi-queries

Conversation

@liangshuo-1

@liangshuo-1 liangshuo-1 commented Apr 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add --queries <csv> to lark-cli contact +search-user for parallel
multi-name fanout. Eliminates the shell-loop pattern when an agent needs
to look up multiple users by keyword in one shot.

Changes

New flag

  • --queries <csv> — comma-separated keywords searched in parallel
    (up to 20 entries, partial-failure tolerant)
  • Mutually exclusive with --query and --user-ids
  • Bool filters (--has-chatted, --exclude-external-users, etc.)
    propagate to every sub-request

Output shape (fanout mode only)

  • data.users[] rows carry matched_query (string)
  • data.queries[] sidecar lists each input with {query, error?, has_more}
  • top-level data.has_more removed (per-query in queries[])
  • error is omitempty — absent on success
  • Single --query mode is byte-for-byte unchanged (regression-guarded)

Reliability

  • Workers run with sync.WaitGroup + buffered semaphore + index-slot
    writes; each has defer recover() converting panics to
    internal error: ... in the sidecar (no stack to stderr)
  • Pre-canceled context returns context canceled without making the
    request
  • All-failed exit propagates first failure's HTTP/API code via ErrAPI;
    falls back to ExitInternal for transport/parse/panic/ctx-canceled
    (avoids emitting code 0 which means success in the Lark protocol)
  • HTTP non-200 ErrMsg now includes a truncated response body for
    diagnosis

Drive-by

  • signature field is now omitempty (mostly empty in practice;
    reduces JSON noise)

Infrastructure

  • internal/httpmock gains BodyFilter / OnMatch / Reusable /
    CapturedBodies hooks for concurrent stub-driven tests
  • internal/output adds users to knownArrayFields so CSV picks the
    primary array correctly when both users and queries are present

Docs

  • skills/lark-contact/references/lark-contact-search-user.md updated
    with fanout section + extended field contract; tightened existing
    copy along the way

Test Plan

  • 26 unit tests covering validation matrix, dedup, single-query
    no-regression, runOneQuery error mapping (transport / HTTP non-200
    / API code != 0 / parse / panic / ctx-canceled), assembler ordering,
    partial and total failure, panic recovery, concurrency cap,
    matched_query fidelity, format-specific output, dry-run
  • go test ./shortcuts/contact/ ./internal/httpmock/ ./internal/output/ -race -count=1 clean
  • go build ./... and go vet ./... clean
  • Real-world smoke against tenant: --queries '梁,test_no_such_user'
    verifies output shape, error omitempty, per-query has_more,
    single-query path unchanged
  • Format coverage exercised end-to-end: json / csv / ndjson / pretty
  • Validation matrix exercised end-to-end: mutex with --query,
    mutex with --user-ids, all-separators, over-20, over-50

Related Issues

N/A

Summary by CodeRabbit

  • New Features

    • Added --queries for parallel multi-query contact search (max 20 queries, ≤50 chars each; mutually exclusive with --query and --user-ids). Results tag each user with matched_query and include a data.queries[] summary.
    • Fanout runs queries concurrently with a bounded concurrency and returns partial-results behavior.
  • Bug Fixes

    • signature field is omitted from JSON output when empty.
  • Formatting

    • Output formatting now prefers users arrays when present.
  • Documentation

    • Updated CLI docs with --queries usage, validation rules, and fanout result semantics.
  • Tests

    • Extensive unit and integration tests for multi-query/fanout behavior and edge cases.

@liangshuo-1 liangshuo-1 added enhancement New feature or request domain/contact PR touches the contact domain size/M Single-domain feat or fix with limited business impact labels Apr 29, 2026
@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 Apr 29, 2026
@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds body-aware matching, per-match hooks, and reusable-hit semantics to the HTTP mock Stub; implements concurrent multi-query fanout for the contact +search-user shortcut with parsing/deduplication, per-query error aggregation, and enriched fanout output; updates output field prioritization and tests/docs for fanout behavior.

Changes

Cohort / File(s) Summary
HTTP Mock Infrastructure
internal/httpmock/registry.go
Extend Stub with BodyFilter, OnMatch, Reusable, and CapturedBodies. RoundTrip buffers/restores request body once, delegates to a new matcher, records captured headers/bodies, invokes OnMatch before response build, and Verify uses captured hits for reusable stubs.
Output Formatting
internal/output/format.go
Prioritizes data["users"] as a recognized array field used by FindArrayField/ExtractItems for NDJSON/table/CSV output extraction.
Search User Shortcut Core
shortcuts/contact/contact_search_user.go
Marks searchUser.Signature as omitempty; adds --queries flag, validation (mutual exclusion with --query/--user-ids, max entries, per-entry length), and routes CLI flow to fanout handling; updates examples and help.
Search User Fanout Implementation
shortcuts/contact/contact_search_user_fanout.go
New fanout flow: parse/trim/dedupe CSV queries (preserve order, case-sensitive), build optional boolean filter, run per-query requests concurrently (bounded), capture per-query failures into results, flatten users with matched_query, return data.queries[] metadata, and only error when all queries fail.
Tests & Docs
shortcuts/contact/contact_search_user_test.go, skills/lark-contact/references/lark-contact-search-user.md
Large test additions for parsing/validation, per-query error shaping, concurrency limits, output formats and regression for omitted empty signature. Docs updated to document --queries, fanout schema (matched_query, data.queries[]), and behavior/exit rules.

Sequence Diagram

sequenceDiagram
    participant User as "User"
    participant CLI as "Runtime (+search-user)"
    participant Parser as "Query Parser"
    participant Executor as "Concurrent Executor"
    participant API as "User API"
    participant Aggregator as "Response Aggregator"
    participant Output as "Output Formatter"

    User->>CLI: invoke `+search-user --queries "alice,bob"`
    CLI->>Parser: parseAndDedupQueries()
    Parser-->>CLI: ["alice","bob"]
    CLI->>Executor: execute queries (bounded concurrency)
    par
      Executor->>API: run query "alice"
      API-->>Executor: users[] or error
    and
      Executor->>API: run query "bob"
      API-->>Executor: users[] or error
    end
    Executor->>Aggregator: per-query results
    Aggregator-->>CLI: combined response (flattened users + queries[])
    CLI->>Output: format results (pretty/table/csv/ndjson)
    Output-->>User: rendered output
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • liuxinyanglxy
  • YangJunzhou-01
  • fangshuyu-768

Poem

🐰 I hopped through mocks and query streams,
I buffered bodies, chased fanout dreams.
Each match I kept, each query traced—
Reusable hops, responses placed.
Tests clap softly; carrots for teams. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.66% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a --queries flag for parallel multi-name fanout to the contact search-user command.
Description check ✅ Passed The description is comprehensive and well-structured, covering all required template sections: Summary explains the motivation, Changes lists the main updates with clear subsections, Test Plan documents thorough verification with checkmarks, and Related Issues is addressed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/search-user-multi-queries

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@liangshuo-1
liangshuo-1 force-pushed the feat/search-user-multi-queries branch from e387a74 to 06b970f Compare April 29, 2026 04:28
@github-actions

github-actions Bot commented Apr 29, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#feat/search-user-multi-queries -y -g

@codecov

codecov Bot commented Apr 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.18868% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.87%. Comparing base (6bb988a) to head (d48af8d).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/contact/contact_search_user_fanout.go 82.51% 21 Missing and 4 partials ⚠️
internal/httpmock/registry.go 55.88% 10 Missing and 5 partials ⚠️
shortcuts/contact/contact_search_user.go 94.28% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #707      +/-   ##
==========================================
+ Coverage   63.80%   63.87%   +0.07%     
==========================================
  Files         500      501       +1     
  Lines       43531    43725     +194     
==========================================
+ Hits        27773    27930     +157     
- Misses      13317    13346      +29     
- Partials     2441     2449       +8     

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

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

Caution

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

⚠️ Outside diff range comments (1)
internal/httpmock/registry.go (1)

78-92: ⚠️ Potential issue | 🔴 Critical

Guard mutex release when BodyFilter panics.

BodyFilter is user-provided and runs while r.mu is held; a panic here skips Line 102 and can leave the registry permanently locked.

🔧 Proposed fix
-	r.mu.Lock()
-	var matched *Stub
-	for _, s := range r.stubs {
+	var matched *Stub
+	func() {
+		r.mu.Lock()
+		defer r.mu.Unlock()
+		for _, s := range r.stubs {
 		if s.matched {
 			continue
 		}
 		if s.Method != "" && s.Method != req.Method {
 			continue
 		}
 		if s.URL != "" && !strings.Contains(urlStr, s.URL) {
 			continue
 		}
 		if s.BodyFilter != nil && !s.BodyFilter(capturedBody) {
 			continue
 		}
 		if !s.Reusable {
 			s.matched = true
 		}
 		s.CapturedHeaders = req.Header.Clone()
 		s.CapturedBody = capturedBody
 		s.CapturedBodies = append(s.CapturedBodies, capturedBody)
 		matched = s
 		break
-	}
-	r.mu.Unlock()
+		}
+	}()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/httpmock/registry.go` around lines 78 - 92, The loop in registry.go
holds r.mu while calling the user-provided BodyFilter on a Stub, so if
BodyFilter panics the mutex r.mu remains locked; modify the matching loop (where
r.mu.Lock() is called and you iterate r.stubs) to ensure r.mu is always unlocked
by using defer r.mu.Unlock() in the function scope or by wrapping the BodyFilter
invocation with a local recover: capture the body, call BodyFilter inside a
small closure that defers a recover and returns (false, err) on panic, and use
that return value to continue; keep references to Stub, r.mu, BodyFilter, and
the matched check so the lock is never left held if BodyFilter panics.
🧹 Nitpick comments (1)
internal/httpmock/registry.go (1)

45-47: Align CapturedBodies behavior with its documented semantics.

The comment says CapturedBodies is for reusable stubs, but Line 98 appends for all stubs. Either gate the append by s.Reusable or update the comment to reflect current behavior.

Also applies to: 98-98

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

In `@internal/httpmock/registry.go` around lines 45 - 47, The comment for
CapturedBodies says it "records every captured request body when Reusable is
set" but the implementation always appends to CapturedBodies; update registry.go
so the append to s.CapturedBodies only happens when s.Reusable is true (leave
s.CapturedBody behavior intact for back-compat), or alternatively update the
comment to reflect that CapturedBodies is appended for all stubs; locate the
append call that pushes the captured bytes (the code referencing
s.CapturedBodies and s.CapturedBody and the s.Reusable flag) and either wrap
that append in an if s.Reusable { ... } or change the field comment to match
current behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/httpmock/registry.go`:
- Around line 73-76: The RoundTrip implementation reads and replaces req.Body
but doesn't close the original body or handle read errors; update the logic in
the method implementing http.RoundTripper.RoundTrip (the block that reads into
capturedBody) to check and handle the error returned by io.ReadAll, call
req.Body.Close() on the original body after reading (or defer Close immediately
after verifying req.Body != nil), and only then set req.Body =
io.NopCloser(bytes.NewReader(capturedBody)); ensure any read error is propagated
or logged instead of being ignored to avoid resource leaks.

In `@skills/lark-contact/references/lark-contact-search-user.md`:
- Line 73: Remove the trailing space inside the inline code span that currently
reads the CSV example with an extra space (the inline code token containing `,,,
`) to satisfy markdownlint MD038; locate the inline code in the markdown
(`lark-contact-search-user.md`) where the CSV example is shown and replace it so
the code span contains exactly three commas with no surrounding internal spaces.

---

Outside diff comments:
In `@internal/httpmock/registry.go`:
- Around line 78-92: The loop in registry.go holds r.mu while calling the
user-provided BodyFilter on a Stub, so if BodyFilter panics the mutex r.mu
remains locked; modify the matching loop (where r.mu.Lock() is called and you
iterate r.stubs) to ensure r.mu is always unlocked by using defer r.mu.Unlock()
in the function scope or by wrapping the BodyFilter invocation with a local
recover: capture the body, call BodyFilter inside a small closure that defers a
recover and returns (false, err) on panic, and use that return value to
continue; keep references to Stub, r.mu, BodyFilter, and the matched check so
the lock is never left held if BodyFilter panics.

---

Nitpick comments:
In `@internal/httpmock/registry.go`:
- Around line 45-47: The comment for CapturedBodies says it "records every
captured request body when Reusable is set" but the implementation always
appends to CapturedBodies; update registry.go so the append to s.CapturedBodies
only happens when s.Reusable is true (leave s.CapturedBody behavior intact for
back-compat), or alternatively update the comment to reflect that CapturedBodies
is appended for all stubs; locate the append call that pushes the captured bytes
(the code referencing s.CapturedBodies and s.CapturedBody and the s.Reusable
flag) and either wrap that append in an if s.Reusable { ... } or change the
field comment to match current behavior.
🪄 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: 8e5bee4f-927f-46f9-beed-1b752ff1f149

📥 Commits

Reviewing files that changed from the base of the PR and between 6bb988a and 06b970f.

📒 Files selected for processing (6)
  • internal/httpmock/registry.go
  • internal/output/format.go
  • shortcuts/contact/contact_search_user.go
  • shortcuts/contact/contact_search_user_fanout.go
  • shortcuts/contact/contact_search_user_test.go
  • skills/lark-contact/references/lark-contact-search-user.md

Comment thread internal/httpmock/registry.go
Comment thread skills/lark-contact/references/lark-contact-search-user.md Outdated
@liangshuo-1
liangshuo-1 force-pushed the feat/search-user-multi-queries branch from 06b970f to cd534ef Compare April 29, 2026 04:55

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

🤖 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/contact/contact_search_user_test.go`:
- Around line 1258-1271: Update the TestFanout_AllFailed_ExitNonZero test to
assert the specific HTTP 500 failure rather than only err != nil: after calling
mountAndRun(t, ContactSearchUser, ...), inspect the returned err (from
mountAndRun/ContactSearchUser path) and assert it encodes the 500 status (e.g.,
unwrap the error to the command/http error type or check err.Error() contains
"500" or "status 500") so the test fails if a different/generic error is
returned; keep references to TestFanout_AllFailed_ExitNonZero, mountAndRun, and
ContactSearchUser so you update the exact failure assertion in that test.

In `@shortcuts/contact/contact_search_user.go`:
- Line 126: The JSON struct tag change for the Signature field may regress
behavior when meta.description is empty; add a regression test that exercises
the JSON serialization path for the ContactSearchUser response (or the existing
JSON-mode test suite) where meta.description == "" and assert that the
"signature" key is omitted (not present) in the serialized JSON rather than
present with an empty string; locate the struct field Signature in
contact_search_user.go and create a test (e.g.,
TestContactSearchUser_JSON_EmptySignature) that marshals the object (or calls
the JSON-mode serializer), inspects the resulting JSON (unmarshal to
map[string]interface{} or check bytes) and fails if "signature" appears with ""
value.
🪄 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: 0090ffa2-ff8c-4064-84c8-22c56fc3909f

📥 Commits

Reviewing files that changed from the base of the PR and between 06b970f and cd534ef.

📒 Files selected for processing (6)
  • internal/httpmock/registry.go
  • internal/output/format.go
  • shortcuts/contact/contact_search_user.go
  • shortcuts/contact/contact_search_user_fanout.go
  • shortcuts/contact/contact_search_user_test.go
  • skills/lark-contact/references/lark-contact-search-user.md
✅ Files skipped from review due to trivial changes (2)
  • internal/output/format.go
  • skills/lark-contact/references/lark-contact-search-user.md

Comment thread shortcuts/contact/contact_search_user_test.go
Comment thread shortcuts/contact/contact_search_user.go
Add --queries CSV flag to lark-cli contact +search-user for parallel
multi-name fanout (up to 20 entries, partial-failure tolerant).

Output shape in fanout mode:
- data.users[] rows carry matched_query (string)
- data.queries[] sidecar lists each input with {query, error?, has_more}
- top-level data.has_more removed (per-query in queries[])
- error is omitempty; absent on success

Single --query mode is byte-for-byte unchanged (regression-guarded).
--queries is mutually exclusive with --query and --user-ids; bool
filters propagate to every sub-request.

Workers run with WaitGroup + buffered semaphore + index-slot writes;
each has defer recover() converting panics to internal error: ... in
the sidecar (no stack to stderr). Pre-canceled context returns
context canceled without making the request.

All-failed exit propagates first failure's HTTP/API code via ErrAPI;
falls back to ExitInternal for transport/parse/panic/ctx-canceled
(avoids emitting code 0, which means success in the Lark protocol).
HTTP non-200 ErrMsg now includes truncated response body for diagnosis.

Drive-by: signature field is now omitempty (mostly empty in practice).

Infrastructure:
- internal/httpmock gains BodyFilter/OnMatch/Reusable/CapturedBodies
  hooks to support concurrent stub-driven tests
- internal/output adds 'users' to knownArrayFields so CSV picks the
  primary array correctly

Change-Id: I3c14195fb8e094ae150002d90c36a0e4a0cc97d0
@liangshuo-1
liangshuo-1 force-pushed the feat/search-user-multi-queries branch from cd534ef to d48af8d Compare April 29, 2026 07:00

@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)
shortcuts/contact/contact_search_user.go (1)

160-161: Clarify the has_more tip for fanout mode.

The current tip wording references --query/top-level has_more; with --queries, has_more is per-query. Consider a short fanout-specific hint to avoid ambiguity.

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

In `@shortcuts/contact/contact_search_user.go` around lines 160 - 161, The help
tip is ambiguous for fanout mode: update the message strings that mention
"--queries" and "has_more" in contact_search_user.go so they explicitly state
that when using --queries (multi-name fanout) has_more is returned per
individual query (not a top-level aggregate); add a brief fanout-specific hint
recommending tightening or adding filters per query if any query's has_more=true
and remind there is no auto-pagination.
🤖 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/contact/contact_search_user.go`:
- Around line 160-161: The help tip is ambiguous for fanout mode: update the
message strings that mention "--queries" and "has_more" in
contact_search_user.go so they explicitly state that when using --queries
(multi-name fanout) has_more is returned per individual query (not a top-level
aggregate); add a brief fanout-specific hint recommending tightening or adding
filters per query if any query's has_more=true and remind there is no
auto-pagination.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 79478bc0-c9bf-40af-bfd3-d7914e831ab6

📥 Commits

Reviewing files that changed from the base of the PR and between cd534ef and d48af8d.

📒 Files selected for processing (6)
  • internal/httpmock/registry.go
  • internal/output/format.go
  • shortcuts/contact/contact_search_user.go
  • shortcuts/contact/contact_search_user_fanout.go
  • shortcuts/contact/contact_search_user_test.go
  • skills/lark-contact/references/lark-contact-search-user.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • shortcuts/contact/contact_search_user_fanout.go
  • skills/lark-contact/references/lark-contact-search-user.md
  • shortcuts/contact/contact_search_user_test.go

@liangshuo-1
liangshuo-1 merged commit f7a56f3 into main Apr 29, 2026
22 checks passed
@liangshuo-1
liangshuo-1 deleted the feat/search-user-multi-queries branch April 29, 2026 09:03
HomyeeKing pushed a commit to HomyeeKing/cli that referenced this pull request May 6, 2026
…e#707)

Add --queries CSV flag to lark-cli contact +search-user for parallel
multi-name fanout (up to 20 entries, partial-failure tolerant).

Output shape in fanout mode:
- data.users[] rows carry matched_query (string)
- data.queries[] sidecar lists each input with {query, error?, has_more}
- top-level data.has_more removed (per-query in queries[])
- error is omitempty; absent on success

Single --query mode is byte-for-byte unchanged (regression-guarded).
--queries is mutually exclusive with --query and --user-ids; bool
filters propagate to every sub-request.

Workers run with WaitGroup + buffered semaphore + index-slot writes;
each has defer recover() converting panics to internal error: ... in
the sidecar (no stack to stderr). Pre-canceled context returns
context canceled without making the request.

All-failed exit propagates first failure's HTTP/API code via ErrAPI;
falls back to ExitInternal for transport/parse/panic/ctx-canceled
(avoids emitting code 0, which means success in the Lark protocol).
HTTP non-200 ErrMsg now includes truncated response body for diagnosis.

Drive-by: signature field is now omitempty (mostly empty in practice).

Infrastructure:
- internal/httpmock gains BodyFilter/OnMatch/Reusable/CapturedBodies
  hooks to support concurrent stub-driven tests
- internal/output adds 'users' to knownArrayFields so CSV picks the
  primary array correctly

Change-Id: I3c14195fb8e094ae150002d90c36a0e4a0cc97d0
tuxedomm pushed a commit to zhumiaoxin/cli that referenced this pull request Jun 6, 2026
…e#707)

Add --queries CSV flag to lark-cli contact +search-user for parallel
multi-name fanout (up to 20 entries, partial-failure tolerant).

Output shape in fanout mode:
- data.users[] rows carry matched_query (string)
- data.queries[] sidecar lists each input with {query, error?, has_more}
- top-level data.has_more removed (per-query in queries[])
- error is omitempty; absent on success

Single --query mode is byte-for-byte unchanged (regression-guarded).
--queries is mutually exclusive with --query and --user-ids; bool
filters propagate to every sub-request.

Workers run with WaitGroup + buffered semaphore + index-slot writes;
each has defer recover() converting panics to internal error: ... in
the sidecar (no stack to stderr). Pre-canceled context returns
context canceled without making the request.

All-failed exit propagates first failure's HTTP/API code via ErrAPI;
falls back to ExitInternal for transport/parse/panic/ctx-canceled
(avoids emitting code 0, which means success in the Lark protocol).
HTTP non-200 ErrMsg now includes truncated response body for diagnosis.

Drive-by: signature field is now omitempty (mostly empty in practice).

Infrastructure:
- internal/httpmock gains BodyFilter/OnMatch/Reusable/CapturedBodies
  hooks to support concurrent stub-driven tests
- internal/output adds 'users' to knownArrayFields so CSV picks the
  primary array correctly

Change-Id: I3c14195fb8e094ae150002d90c36a0e4a0cc97d0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/contact PR touches the contact domain enhancement New feature or request 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