Skip to content

feat(contact): emit typed error envelopes across the contact domain#1287

Merged
evandance merged 1 commit into
mainfrom
feat/errs-migrate-contact
Jun 9, 2026
Merged

feat(contact): emit typed error envelopes across the contact domain#1287
evandance merged 1 commit into
mainfrom
feat/errs-migrate-contact

Conversation

@evandance

@evandance evandance commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

shortcuts/contact still returned command-facing failures through legacy output.Err*, legacy common validation helpers, and legacy API-call envelopes. This migrates the contact domain to typed errs.* envelopes so callers get stable type, subtype, param / params, code, hint, retryable, and log_id metadata across validation, API, network, invalid-response, and fanout all-failed flows.

Changes

  • Migrate contact validation from common.FlagErrorf, ResolveOpenIDs, and ValidateUserID to typed validation errors with param / params.
  • Convert contact API boundaries from legacy runtime.CallAPI / manual response parsing to runtime.CallAPITyped or runtime.ClassifyAPIResponse.
  • Add shortcuts/contact/contact_errors.go with contact-local typed helpers for invalid responses and fanout error summary/all-failed decoration, matching the domain-helper pattern used by migrated drive/mail domains.
  • Preserve +search-user --queries partial-failure stdout shape while keeping the original typed error for the all-failed top-level return; typed code, subtype, log_id, retryable, and hint are preserved via errs.ProblemOf.
  • Update contact tests to assert typed problems instead of legacy output.ExitError details, and add coverage for +get-user typed validation/API paths plus contact-local error helpers.
  • Enforce typed-only contact errors through .golangci.yml path exceptions and the errscontract legacy-envelope / legacy-common-helper guards.

Test Plan

  • gofmt -l .
  • go test ./shortcuts/contact ./internal/errclass
  • go test -race ./shortcuts/contact ./internal/errclass
  • go test -cover ./shortcuts/contact — 89.8% package statement coverage
  • go vet ./...
  • go run -C lint . ..
  • go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 run --new-from-rev=origin/main — 0 issues
  • go mod tidy produced no go.mod / go.sum diff

Related Issues

N/A

Summary by CodeRabbit

  • Bug Fixes

    • Validation and parameter errors for contact commands improved (structured validation problems for booleans, sizes, and missing params); bot current-user validation now reports the correct param.
    • Fanout/query failures produce clearer summaries while preserving underlying error causes.
  • Refactor

    • Contact shortcuts now classify/decode typed API responses and use unified typed error flow instead of manual status/code handling.
  • New Features

    • Added helpers to format and aggregate fanout errors with retry hints.
  • Tests

    • Expanded tests covering typed validation, API failure/success cases, fanout error handling, and decoding.
  • Chores

    • Linter config updated to exempt contact shortcut paths.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Lost in the diff? Review this PR in Change Stack to follow the change map from intent to exact ranges.

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

Migrate contact shortcuts to typed errs and classified API responses: expand lint migration scopes, add contact fanout error helpers and tests, switch CallAPI to CallAPITyped, refactor search validation/decoding, preserve fanout underlying errors, and update tests to assert typed problems.

Changes

Typed Error Migration for Contact Domain

Layer / File(s) Summary
Linter configuration
.golangci.yml, lint/errscontract/rule_no_legacy_common_helper_call.go, lint/errscontract/rule_no_legacy_envelope_literal.go
Register shortcuts/contact/ as migrated across forbidigo rules and update rule allowlists.
Contact error helpers and tests
shortcuts/contact/contact_errors.go, shortcuts/contact/contact_errors_test.go
Add internal fanout error helpers (summary, all-failed construction, rune-safe truncation) and unit tests validating HTTP-summary formatting and typed-problem preservation.
Get-user API and validation migration
shortcuts/contact/contact_get_user.go, shortcuts/contact/contact_get_user_test.go
Switch three API calls to runtime.CallAPITyped and change bot-without---user-id validation to common.ValidationErrorf with WithParam; add tests asserting typed validation and typed API failure handling.
Search-user single execution, decoder, and validation
shortcuts/contact/contact_search_user.go
Refactor executeSearchUserSingle to use runtime.ClassifyAPIResponse and decodeSearchUserAPIData; add decoder helper; change validation to common.ValidationErrorf with errs.InvalidParam entries; use ResolveOpenIDsTyped.
Fanout result shape and error propagation
shortcuts/contact/contact_search_user_fanout.go
Extend fanoutResult to preserve original Err, refactor runOneQuery to return fanoutErrorResult with ErrMsg and Err, and change buildFanoutResponse to return contactFanoutAllFailedError(firstErr, msg) when all queries fail.
Search-user and get-user tests updated to typed problems
shortcuts/contact/contact_search_user_test.go, shortcuts/contact/contact_get_user_test.go
Replace exit-error/detail assertions with errs.ProblemOf-based checks and add targeted tests for decoding failures, fanout behaviors, dry-run shapes, and typed validation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • larksuite/cli#1242: Related migration of shortcut paths into lint migrated-paths and common helper changes.
  • larksuite/cli#648: Prior changes touching contact_search_user request/response handling and typed search logic this PR builds upon.
  • larksuite/cli#1248: Similar lint migrated-path scope updates for legacy-envelope rules in other shortcut domains.

Suggested labels

size/L, feature

Suggested reviewers

  • liuxinyanglxy
  • liangshuo-1
  • kongenpei

"I hopped through errors, neat and spry,
Typed problems now tell the why,
Fanout keeps the original cause,
Linters mark the migrated clause,
Tests clap loud — the rabbit’s high!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.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 clearly and accurately describes the main change: migrating the contact domain to typed error envelopes, which is the primary objective of this changeset.
Description check ✅ Passed The description comprehensively covers all required sections with specific implementation details, changes, and test plan, matching the repository template and providing sufficient context for review.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/errs-migrate-contact

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/contact PR touches the contact domain size/L Large or sensitive change across domains or core paths labels Jun 5, 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)
shortcuts/contact/contact_search_user_fanout.go (1)

222-349: 💤 Low value

Consider extracting repeated builder logic to reduce duplication.

All 8 switch cases share identical conditional-chaining logic (WithCode, WithLogID, WithHint, WithRetryable). A small helper could cut this to ~30 lines while preserving behavior.

♻️ Optional refactor sketch
// applyMetadata applies optional fields and returns an error interface.
func applyMetadata(e interface {
	WithCode(int) interface{ WithLogID(string) interface{ WithHint(string, ...any) interface{ WithRetryable() error } } }
	// ... or define a shared TypedErrorBuilder interface in errs package
}, first fanoutResult) error {
	if first.ErrCode != 0 {
		e = e.WithCode(first.ErrCode)
	}
	if first.ErrLog != "" {
		e = e.WithLogID(first.ErrLog)
	}
	if first.ErrHint != "" {
		e = e.WithHint("%s", first.ErrHint)
	}
	if first.ErrTry {
		return e.WithRetryable()
	}
	return e.(error)
}

func fanoutAllFailedError(first fanoutResult, msg string) error {
	subtype := first.ErrSub
	if subtype == "" {
		subtype = errs.SubtypeUnknown
	}
	var base interface{ ... } // typed error
	switch first.ErrCat {
	case errs.CategoryAuthentication:
		base = errs.NewAuthenticationError(subtype, "%s", msg)
	case errs.CategoryAuthorization:
		base = errs.NewPermissionError(subtype, "%s", msg)
	// ... other cases
	default:
		base = errs.NewAPIError(subtype, "%s", msg)
	}
	return applyMetadata(base, first)
}

This depends on whether the errs package exposes a common builder interface. If not, the current explicit approach is fine—just verbose.

🤖 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/contact/contact_search_user_fanout.go` around lines 222 - 349, The
fanoutAllFailedError function repeats identical builder chaining across each
error case; extract that duplication by creating a helper (e.g., applyMetadata)
that accepts the error builder returned from errs.NewAuthenticationError /
NewPermissionError / NewConfigError / NewNetworkError / NewValidationError /
NewSecurityPolicyError / NewInternalError / NewAPIError and the fanoutResult,
applies WithCode, WithLogID, WithHint and WithRetryable conditionally, and
returns the final error; you can implement a small interface for the builder
either locally or in the errs package to type the helper, then simplify
fanoutAllFailedError to only choose the base error by ErrCat and call
applyMetadata(base, first).
🤖 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/contact/contact_search_user_fanout.go`:
- Around line 222-349: The fanoutAllFailedError function repeats identical
builder chaining across each error case; extract that duplication by creating a
helper (e.g., applyMetadata) that accepts the error builder returned from
errs.NewAuthenticationError / NewPermissionError / NewConfigError /
NewNetworkError / NewValidationError / NewSecurityPolicyError / NewInternalError
/ NewAPIError and the fanoutResult, applies WithCode, WithLogID, WithHint and
WithRetryable conditionally, and returns the final error; you can implement a
small interface for the builder either locally or in the errs package to type
the helper, then simplify fanoutAllFailedError to only choose the base error by
ErrCat and call applyMetadata(base, first).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 117150c8-65bb-492e-9a4b-dd6c7e1b7ab0

📥 Commits

Reviewing files that changed from the base of the PR and between f3949f0 and 1bf7eef.

📒 Files selected for processing (7)
  • .golangci.yml
  • lint/errscontract/rule_no_legacy_common_helper_call.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/contact/contact_get_user.go
  • shortcuts/contact/contact_search_user.go
  • shortcuts/contact/contact_search_user_fanout.go
  • shortcuts/contact/contact_search_user_test.go

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#feat/errs-migrate-contact -y -g

@codecov

codecov Bot commented Jun 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.39496% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.58%. Comparing base (ed3fe93) to head (20748e5).

Files with missing lines Patch % Lines
shortcuts/contact/contact_search_user.go 87.27% 4 Missing and 3 partials ⚠️
shortcuts/contact/contact_errors.go 88.37% 5 Missing ⚠️
shortcuts/contact/contact_search_user_fanout.go 87.50% 1 Missing and 1 partial ⚠️
shortcuts/contact/contact_get_user.go 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1287      +/-   ##
==========================================
+ Coverage   71.47%   71.58%   +0.10%     
==========================================
  Files         688      689       +1     
  Lines       65467    65521      +54     
==========================================
+ Hits        46791    46901     +110     
+ Misses      15031    14972      -59     
- Partials     3645     3648       +3     

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

@evandance evandance force-pushed the feat/errs-migrate-contact branch from 1bf7eef to 3dced30 Compare June 6, 2026 03:11
@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 Jun 6, 2026
@evandance evandance changed the title fix: migrate contact errors to typed taxonomy feat(contact): return typed error envelopes across contact shortcuts Jun 6, 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: 2

🤖 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/contact/contact_errors_test.go`:
- Around line 27-47: The test fails because contactFanoutAllFailedError
currently mutates the extracted typed problem's Message but returns an error
that does not preserve that updated Message; update contactFanoutAllFailedError
to extract the typed problem (using errs.ProblemOf), set p.Message to the new
decorated text, then reconstruct and return a new typed error that preserves all
original metadata (Category, Subtype, Code, LogID, Retryable, etc.) and includes
the updated Message so callers see the decorated message instead of the original
"rate limit". Ensure the returned value is a typed errs.APIError (or the same
concrete typed error used originally) rather than the unmodified original error.

In `@shortcuts/contact/contact_errors.go`:
- Around line 39-45: The code in contactFanoutAllFailedError mutates p.Message
returned by errs.ProblemOf(err) but ProblemOf returns a copy, so the mutation is
lost; replace this by reconstructing and returning a new typed error that
preserves the original error's metadata while setting Message to msg, e.g. use
errs.ProblemOf(err) to extract fields then use a type switch on the concrete
error type returned by err (APIError, NetworkError, etc.) to build the
corresponding typed error value with the same
category/subtype/code/log_id/retryable and the new message, and finally attach
WithHint(contactFanoutRetryHint) and WithCause(err) before returning; ensure
contactFanoutAllFailedError returns the reconstructed typed error so tests
expecting "all N queries failed" in p.Message pass.
🪄 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: df69329e-0766-4be6-b680-ad7fe067cd99

📥 Commits

Reviewing files that changed from the base of the PR and between 1bf7eef and 3dced30.

📒 Files selected for processing (10)
  • .golangci.yml
  • lint/errscontract/rule_no_legacy_common_helper_call.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/contact/contact_errors.go
  • shortcuts/contact/contact_errors_test.go
  • shortcuts/contact/contact_get_user.go
  • shortcuts/contact/contact_get_user_test.go
  • shortcuts/contact/contact_search_user.go
  • shortcuts/contact/contact_search_user_fanout.go
  • shortcuts/contact/contact_search_user_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • .golangci.yml
  • shortcuts/contact/contact_get_user.go
  • shortcuts/contact/contact_search_user.go

Comment thread shortcuts/contact/contact_errors_test.go
Comment thread shortcuts/contact/contact_errors.go
@evandance evandance force-pushed the feat/errs-migrate-contact branch 2 times, most recently from 0ded2f8 to bfff947 Compare June 6, 2026 03:56

@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/contact/contact_search_user.go`:
- Around line 247-258: The function decodeSearchUserAPIData currently marshals a
nil/non-object data into "null" and silently decodes it to a zero-value
searchUserAPIData; fix this by explicitly rejecting missing/ nil data before
marshaling: in decodeSearchUserAPIData check if the input data is nil (or
otherwise clearly absent) and return contactInvalidResponseError("missing search
user response data") (preserving WithCause where appropriate) instead of
proceeding to json.Marshal, so malformed success bodies without a data object
are treated as an invalid response rather than an empty result.
🪄 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: d63d92f3-3d26-4044-aca1-8c45fc8852ad

📥 Commits

Reviewing files that changed from the base of the PR and between 0ded2f8 and bfff947.

📒 Files selected for processing (10)
  • .golangci.yml
  • lint/errscontract/rule_no_legacy_common_helper_call.go
  • lint/errscontract/rule_no_legacy_envelope_literal.go
  • shortcuts/contact/contact_errors.go
  • shortcuts/contact/contact_errors_test.go
  • shortcuts/contact/contact_get_user.go
  • shortcuts/contact/contact_get_user_test.go
  • shortcuts/contact/contact_search_user.go
  • shortcuts/contact/contact_search_user_fanout.go
  • shortcuts/contact/contact_search_user_test.go
✅ Files skipped from review due to trivial changes (2)
  • .golangci.yml
  • lint/errscontract/rule_no_legacy_envelope_literal.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • shortcuts/contact/contact_get_user.go
  • shortcuts/contact/contact_errors.go

Comment thread shortcuts/contact/contact_search_user.go
@evandance evandance force-pushed the feat/errs-migrate-contact branch from bfff947 to 59dca34 Compare June 6, 2026 04:12
@evandance evandance changed the title feat(contact): return typed error envelopes across contact shortcuts feat(contact): emit typed error envelopes across the contact domain Jun 6, 2026
@evandance evandance force-pushed the feat/errs-migrate-contact branch 4 times, most recently from 15b7e89 to b74056b Compare June 8, 2026 08:37
@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 8, 2026
@evandance evandance force-pushed the feat/errs-migrate-contact branch from b74056b to d8bd1df Compare June 9, 2026 03:42
@evandance evandance force-pushed the feat/errs-migrate-contact branch from d8bd1df to 20748e5 Compare June 9, 2026 03:47
@evandance evandance merged commit 03ea6e7 into main Jun 9, 2026
28 of 30 checks passed
@evandance evandance deleted the feat/errs-migrate-contact branch June 9, 2026 04:07
@coderabbitai coderabbitai Bot mentioned this pull request Jun 11, 2026
3 tasks
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 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