Skip to content

feat: add mail sender allow/block list shortcuts#1599

Open
infeng wants to merge 5 commits into
larksuite:mainfrom
infeng:feat/71d5802
Open

feat: add mail sender allow/block list shortcuts#1599
infeng wants to merge 5 commits into
larksuite:mainfrom
infeng:feat/71d5802

Conversation

@infeng

@infeng infeng commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Add mail sender allow/block list shortcuts to the lark-cli mail domain: list, search, add, and remove allow-list and block-list sender entries, with combined or filtered views, paging, and exact-match search.

Changes

  • shortcuts/mail/mail_sender_allow_block.go — new shortcuts to list / search / add / remove allow- and block-list sender entries (with tests)
  • shortcuts/mail/shortcuts.go — register the new shortcuts
  • Input validation for email address, list selection, and per-list address limits; clearer error messaging with a retry hint when sender data is temporarily unavailable
  • skills/lark-mail/ references + skill-template/domains/mail.md — usage docs and examples for the new commands

Summary by CodeRabbit

  • New Features

    • Added mail sender allow/block shortcuts for listing, searching, adding, and removing sender entries.
    • Added support for viewing all lists together or filtering by allow/block, with paging and exact-match search options.
    • Expanded mail documentation with usage examples, parameter details, and expected results for the new commands.
  • Bug Fixes

    • Improved validation for email input, list selection, and address limits.
    • Added clearer error messaging when sender data is temporarily unavailable, including a retry hint.

infeng added 5 commits June 26, 2026 12:55
Add user-level mail sender allow/block shortcuts for listing, querying, setting, and deleting entries. The commands mirror the published user_mailbox allow_sender and blocked_sender APIs and surface retry guidance for warming search caches.

sprint: S7
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Added four mail sender allow/block shortcuts, wired them into the mail shortcut registry, and updated the related mail docs and references. The implementation covers list, query, set, and delete flows, with validation, pagination, dry-run behavior, structured outputs, and tests.

Changes

Mail sender allow/block shortcuts

Layer / File(s) Summary
Command surface and docs
shortcuts/mail/mail_sender_allow_block.go, shortcuts/mail/shortcuts.go, skill-template/domains/mail.md, skills/lark-mail/SKILL.md, skills/lark-mail/references/lark-mail-sender-*.md
Adds sender allow/block constants, exports the four new shortcuts, and updates the mail skill and reference docs for the new sender list commands and type semantics.
Validation and request shapes
shortcuts/mail/mail_sender_allow_block.go, shortcuts/mail/mail_sender_allow_block_test.go
Adds mailbox/type/query/address validation, email parsing and deduplication helpers, request parameter builders, and tests for parsing, validation errors, and dry-run payloads.
List and query reads
shortcuts/mail/mail_sender_allow_block.go, shortcuts/mail/mail_sender_allow_block_test.go
Implements sender list and query execution, pagination aggregation, exact-match filtering, output formatting, API 456 retry-hint handling, and execution tests.
Set and delete writes
shortcuts/mail/mail_sender_allow_block.go, shortcuts/mail/mail_sender_allow_block_test.go
Implements batch create and batch remove flows for sender entries and tests the returned failed-item and deleted-count outputs.

Sequence Diagram(s)

sequenceDiagram
  participant MailSenderList
  participant MailSenderQuery
  participant "sender list endpoint"
  participant outputSenderList

  MailSenderList->>sender list endpoint: GET allow/block pages
  sender list endpoint-->>MailSenderList: items, total, has_more, next_page_token
  MailSenderList->>outputSenderList: print table

  MailSenderQuery->>sender list endpoint: GET keyword search
  sender list endpoint-->>MailSenderQuery: matching items
  MailSenderQuery->>outputSenderList: print filtered results
Loading
sequenceDiagram
  participant MailSenderSet
  participant MailSenderDelete
  participant "net/mail"
  participant "batch_create endpoint"
  participant "batch_remove endpoint"

  MailSenderSet->>net/mail: parse and validate addresses
  MailSenderSet->>batch_create endpoint: POST batch_create
  batch_create endpoint-->>MailSenderSet: failed_items

  MailSenderDelete->>net/mail: parse and validate addresses
  MailSenderDelete->>batch_remove endpoint: POST batch_remove
  batch_remove endpoint-->>MailSenderDelete: deleted_count
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

enhancement

Suggested reviewers

  • chanthuang

Poem

A bunny hopped through allow and block,
with sender lists that tick and tock.
Query, set, and delete ran neat,
with pagination on rabbit feet. 🐰

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The PR description covers Summary and Changes, but it omits the required Test Plan and Related Issues sections from the repository template. Add a Test Plan with unit/manual verification steps and include Related Issues (or explicitly mark them as None) to match the template.
✅ Passed checks (3 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding mail sender allow/block list shortcuts.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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/mail PR touches the mail domain size/L Large or sensitive change across domains or core paths 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.

Actionable comments posted: 4

🤖 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/mail/mail_sender_allow_block_test.go`:
- Around line 16-31: The helper assertValidationError only checks
output.ExitError and text, so it can miss regressions in typed metadata and
cause preservation. Update the mail shortcut error-path tests to assert
errs.ProblemOf(err) for category/subtype fields, use errors.As to verify any
Param on *errs.ValidationError separately, and keep a check that the 456 path
still unwraps to the original cause.

In `@shortcuts/mail/mail_sender_allow_block.go`:
- Around line 436-445: The error handling in decorateSenderAPIError is
rebuilding lower-layer failures into a new generic output error, which loses the
original API classification and cause. Update this helper to pass through
already-typed errors from runtime.DoAPIJSON unchanged, switch callers in
shortcuts/mail/mail_sender_allow_block.go to use runtime.CallAPITyped for Lark
API failures, and when enriching the 456 case preserve the original error chain
by attaching the cause with .WithCause(err) instead of creating a fresh
replacement error.
- Around line 333-338: `--type all` pagination is only storing one cursor, but
`senderReadQuery`/the `NextPageToken` handling in `mail_sender_allow_block.go`
needs to support separate cursors for allow and blocked lists. Update the read
path and command surface so `NextPageTokens` are preserved per list and replayed
independently, and make the `senderReadQuery` logic consume the correct token
for each of `allow_senders` and `blocked_senders` instead of applying a single
`--page-token` to both.
- Around line 261-269: `validateBotMailboxNotMe` and `mailValidationParamError`
are using untyped `output.ErrValidation`, which breaks the shortcut error
contract and loses the failing input metadata. Update both helpers to return
typed `errs.ValidationError` values instead, and populate the `param` field with
the relevant user-facing flag/input so callers can identify the invalid
argument. Keep the existing logic in `validateBotMailboxNotMe` and
`mailValidationParamError`, but route the error creation through the typed
`errs` package used by other shortcut validation paths.
🪄 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: 951ae50c-a1c2-4eb9-a902-a5d383ba7933

📥 Commits

Reviewing files that changed from the base of the PR and between 6b83325 and c716979.

📒 Files selected for processing (9)
  • shortcuts/mail/mail_sender_allow_block.go
  • shortcuts/mail/mail_sender_allow_block_test.go
  • shortcuts/mail/shortcuts.go
  • skill-template/domains/mail.md
  • skills/lark-mail/SKILL.md
  • skills/lark-mail/references/lark-mail-sender-delete.md
  • skills/lark-mail/references/lark-mail-sender-list.md
  • skills/lark-mail/references/lark-mail-sender-query.md
  • skills/lark-mail/references/lark-mail-sender-set.md

Comment on lines +16 to +31
func assertValidationError(t *testing.T, err error, wantSubstr string) {
t.Helper()
if err == nil {
t.Fatal("expected validation error, got nil")
}
var exitErr *output.ExitError
if !errors.As(err, &exitErr) {
t.Fatalf("expected output.ExitError, got %T: %v", err, err)
}
if exitErr.Code != output.ExitValidation {
t.Fatalf("exit code = %d, want ExitValidation", exitErr.Code)
}
if wantSubstr != "" && !strings.Contains(exitErr.Error(), wantSubstr) {
t.Fatalf("error = %q, want substring %q", exitErr.Error(), wantSubstr)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Make the new error-path tests assert typed metadata.

These assertions only check output.ExitError and message text, so they will still pass if category, subtype, param, or cause preservation regresses. Once the shortcut returns typed errs.*, assert problem fields via errs.ProblemOf(err), use errors.As(...*errs.ValidationError) for Param, and verify the 456 path still unwraps the original cause.

As per coding guidelines, **/*_test.go: error-path tests must assert typed metadata and cause preservation. Based on learnings, errs.ProblemOf does not expose Param, so that field needs a separate errors.As assertion on *errs.ValidationError.

Also applies to: 294-303

🤖 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/mail/mail_sender_allow_block_test.go` around lines 16 - 31, The
helper assertValidationError only checks output.ExitError and text, so it can
miss regressions in typed metadata and cause preservation. Update the mail
shortcut error-path tests to assert errs.ProblemOf(err) for category/subtype
fields, use errors.As to verify any Param on *errs.ValidationError separately,
and keep a check that the 456 path still unwraps to the original cause.

Sources: Coding guidelines, Learnings

Comment on lines +261 to +269
func validateBotMailboxNotMe(runtime *common.RuntimeContext) error {
if runtime.IsBot() && strings.EqualFold(strings.TrimSpace(runtime.Str("mailbox")), "me") {
return output.ErrValidation("bot identity does not support --mailbox me; pass an explicit mailbox email address")
}
return nil
}

func mailValidationParamError(flag, format string, args ...interface{}) error {
return output.ErrValidation("%s: %s", flag, fmt.Sprintf(format, args...))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Switch these validation helpers to typed errs.ValidationErrors.

validateBotMailboxNotMe and mailValidationParamError currently return output.ErrValidation, which violates the shortcut error contract and drops structured param metadata for the failing flag. That also matches the repo’s errs-typed-only lint failure pattern for changed shortcuts/ code.

Suggested direction
+import "github.com/larksuite/cli/internal/errs"
+
 func validateBotMailboxNotMe(runtime *common.RuntimeContext) error {
 	if runtime.IsBot() && strings.EqualFold(strings.TrimSpace(runtime.Str("mailbox")), "me") {
-		return output.ErrValidation("bot identity does not support --mailbox me; pass an explicit mailbox email address")
+		return errs.NewValidationError(
+			errs.SubtypeInvalidArgument,
+			"bot identity does not support --mailbox me",
+		).WithParam("--mailbox").WithHint("pass an explicit mailbox email address")
 	}
 	return nil
 }
 
 func mailValidationParamError(flag, format string, args ...interface{}) error {
-	return output.ErrValidation("%s: %s", flag, fmt.Sprintf(format, args...))
+	return errs.NewValidationError(
+		errs.SubtypeInvalidArgument,
+		fmt.Sprintf(format, args...),
+	).WithParam(flag)
 }

As per coding guidelines, **/*.go: command-facing failures must use typed errs.* errors and the param field should name the user input that failed. Based on learnings, changed shortcuts/ code using output.Err* hits the forbidigo [errs-typed-only] rule.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func validateBotMailboxNotMe(runtime *common.RuntimeContext) error {
if runtime.IsBot() && strings.EqualFold(strings.TrimSpace(runtime.Str("mailbox")), "me") {
return output.ErrValidation("bot identity does not support --mailbox me; pass an explicit mailbox email address")
}
return nil
}
func mailValidationParamError(flag, format string, args ...interface{}) error {
return output.ErrValidation("%s: %s", flag, fmt.Sprintf(format, args...))
func validateBotMailboxNotMe(runtime *common.RuntimeContext) error {
if runtime.IsBot() && strings.EqualFold(strings.TrimSpace(runtime.Str("mailbox")), "me") {
return errs.NewValidationError(
errs.SubtypeInvalidArgument,
"bot identity does not support --mailbox me",
).WithParam("--mailbox").WithHint("pass an explicit mailbox email address")
}
return nil
}
func mailValidationParamError(flag, format string, args ...interface{}) error {
return errs.NewValidationError(
errs.SubtypeInvalidArgument,
fmt.Sprintf(format, args...),
).WithParam(flag)
}
🤖 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/mail/mail_sender_allow_block.go` around lines 261 - 269,
`validateBotMailboxNotMe` and `mailValidationParamError` are using untyped
`output.ErrValidation`, which breaks the shortcut error contract and loses the
failing input metadata. Update both helpers to return typed
`errs.ValidationError` values instead, and populate the `param` field with the
relevant user-facing flag/input so callers can identify the invalid argument.
Keep the existing logic in `validateBotMailboxNotMe` and
`mailValidationParamError`, but route the error creation through the typed
`errs` package used by other shortcut validation paths.

Sources: Coding guidelines, Learnings

Comment on lines +333 to +338
token := strVal(data["page_token"])
if token != "" {
out.NextPageTokens[currentType] = token
if listType != senderListAll {
out.NextPageToken = token
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

--type all pagination is not resumable.

The read path returns per-list cursors in next_page_tokens, but the command surface only accepts one --page-token, and senderReadQuery replays that single token to both allow_senders and blocked_senders. As soon as the two lists diverge, page 2 for +sender-list/+sender-query --type all cannot be fetched correctly.

Also applies to: 361-370

🤖 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/mail/mail_sender_allow_block.go` around lines 333 - 338, `--type
all` pagination is only storing one cursor, but `senderReadQuery`/the
`NextPageToken` handling in `mail_sender_allow_block.go` needs to support
separate cursors for allow and blocked lists. Update the read path and command
surface so `NextPageTokens` are preserved per list and replayed independently,
and make the `senderReadQuery` logic consume the correct token for each of
`allow_senders` and `blocked_senders` instead of applying a single
`--page-token` to both.

Comment on lines +436 to +445
func decorateSenderAPIError(err error, action string) error {
var exitErr *output.ExitError
if errors.As(err, &exitErr) && exitErr.Detail != nil && exitErr.Detail.Code == 456 {
withHint := output.ErrWithHint(exitErr.Code, exitErr.Detail.Type, exitErr.Detail.Message, "search cache warming; retry later")
withHint.Detail.Code = exitErr.Detail.Code
withHint.Detail.Detail = exitErr.Detail.Detail
return withHint
}
return output.Errorf(output.ExitAPI, "api_error", "%s failed: %v", action, err)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve typed lower-layer API errors instead of rebuilding them here.

This helper rewrites every runtime.DoAPIJSON failure into a fresh output error, so network/auth/API classifications get collapsed to a generic API failure. The 456 branch also drops Unwrap() semantics by returning a new hinted error without the original cause.

As per coding guidelines, shortcuts/**/*.go should use runtime.CallAPITyped for Lark API failures, pass through already-typed lower-layer errors unchanged, and preserve causes with .WithCause(err).

🤖 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/mail/mail_sender_allow_block.go` around lines 436 - 445, The error
handling in decorateSenderAPIError is rebuilding lower-layer failures into a new
generic output error, which loses the original API classification and cause.
Update this helper to pass through already-typed errors from runtime.DoAPIJSON
unchanged, switch callers in shortcuts/mail/mail_sender_allow_block.go to use
runtime.CallAPITyped for Lark API failures, and when enriching the 456 case
preserve the original error chain by attaching the cause with .WithCause(err)
instead of creating a fresh replacement error.

Source: Coding guidelines

@infeng infeng changed the title fix: keep sender shortcuts compatible with fork base feat: add mail sender allow/block list shortcuts Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/mail PR touches the mail 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.

1 participant