Skip to content

feat(apps): add role management shortcuts#1881

Open
linchao5102 wants to merge 3 commits into
mainfrom
feat/apps-role-management-framework
Open

feat(apps): add role management shortcuts#1881
linchao5102 wants to merge 3 commits into
mainfrom
feat/apps-role-management-framework

Conversation

@linchao5102

@linchao5102 linchao5102 commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add first-class app role management to the apps shortcut domain, including role CRUD, typed member operations, and user-role matching. The commands preserve structured error contracts, require explicit confirmation for destructive operations, and keep machine-readable output suitable for AI-agent consumers.

Changes

  • Add nine role shortcuts: +role-list, +role-get, +role-create, +role-update, +role-delete, +role-member-list, +role-member-add, +role-member-remove, and +role-match-list.
  • Add strict app, role, member, pagination, and response validation, including exact-name role resolution, atomic member writes, safe delete acknowledgements, and a narrow chat-filter compatibility fallback.
  • Update the lark-apps Skill and role reference with role routing, high-impact confirmation, readback, and member-resolution guidance.
  • Add unit tests plus dry-run and environment-gated live E2E coverage for role and member workflows.
  • Correct the documented department ID prefix from od_ to od- and refresh the selected Apps E2E coverage accounting.

Test Plan

  • Unit tests pass (make unit-test)
  • Build passes (make build)
  • go vet ./...
  • gofmt -l . produces no output
  • go mod tidy leaves go.mod and go.sum unchanged
  • go test ./tests/cli_e2e/apps -count=1
  • Manual local verification confirms the lark-cli apps +role-* lifecycle and member-management flows work as expected
  • CI against the latest main (pending)

Related Issues

  • None

Summary by CodeRabbit

  • New Features
    • Added CLI commands to list, view, create, update, and delete app roles.
    • Added commands to list role members, add/remove members, and find roles matching a user.
    • Included robust pagination, exact-name filtering, input validation, safe confirmations, and structured/pretty output formatting.
  • Documentation
    • Added/expanded end-to-end guidance for app RBAC role and role-member workflows, including execution rules and verification steps.
  • Bug Fixes
    • Corrected department identifier formatting to consistently use the od- form.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds nine Apps CLI shortcuts for role CRUD, role-member management, and role matching, with shared validation, pagination, normalization, rendering, documentation, unit tests, registry tests, and fixture-gated E2E workflows. Department identifier fixtures now use dash syntax.

Changes

Apps role management

Layer / File(s) Summary
Role contracts and validation
shortcuts/apps/apps_role_common.go, shortcuts/apps/apps_role_common_test.go
Adds role, app, member, pagination, response, sanitization, and error-hint validation helpers with focused tests.
Role CRUD commands
shortcuts/apps/apps_role.go, shortcuts/apps/apps_role_test.go
Adds role list/get/create/update/delete commands, exact-name scanning, response normalization, rendering, and CRUD tests.
Role members and matching
shortcuts/apps/apps_role_member.go, shortcuts/apps/apps_role_member_test.go
Adds member list/add/remove and role-match commands with filtering, retry handling, normalization, formatting, and execution tests.
Shortcut registration and registry tests
shortcuts/apps/shortcuts.go, shortcuts/apps/shortcuts_test.go
Registers role commands and verifies the expanded visible shortcut set.
Role execution guidance
skills/lark-apps/SKILL.md, skills/lark-apps/references/lark-apps-role.md, skills/lark-apps/references/lark-apps-access-scope-set.md
Documents role identifiers, serialized execution, pagination, member operations, confirmation, readback, and department ID syntax.
End-to-end workflows and coverage
tests/cli_e2e/apps/apps_role_management_test.go, tests/cli_e2e/apps/coverage.md
Adds dry-run, validation, fixture-gated live workflows, and coverage records for role operations.
Spark error metadata
internal/errclass/codemeta_spark.go, internal/errclass/codemeta_spark_test.go
Registers and tests Spark role error-code classifications.
Department identifier consistency
shortcuts/apps/apps_access_scope_get_test.go, internal/qualitygate/rules/dryrun.go, internal/qualitygate/rules/dryrun_test.go
Updates department fixtures and placeholder values from od_ to od- syntax.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • larksuite/cli#1002: Updates the overlapping Apps access-scope department fixture to use od-z.

Suggested labels: feature, domain/ccm

Suggested reviewers: liangshuo-1

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.91% 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 summarizes the main change: adding app role management shortcuts.
Description check ✅ Passed The description matches the template with Summary, Changes, Test Plan, and Related Issues filled in.
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
📝 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-role-management-framework

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 the size/XL Architecture-level or global-impact change label Jul 14, 2026
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#feat/apps-role-management-framework -y -g

@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/apps/apps_role_common.go`:
- Around line 275-278: Remove the single-flag param metadata from the multi-flag
validation errors: in shortcuts/apps/apps_role_common.go lines 275-278, update
the validation error for the total users/departments/chats check; in
shortcuts/apps/apps_role.go lines 193-195, update the validation error for the
alternative update fields; and in shortcuts/apps/apps_role_member.go lines
370-372, update the validation error for member flags or --all. Each error
should omit param while preserving its message and hint.

In `@shortcuts/apps/apps_role_test.go`:
- Around line 898-907: Extend the error assertions after AppsRoleList.Execute in
shortcuts/apps/apps_role_test.go (898-907) to verify the typed problem’s
category and subtype in addition to roleAppHint. Apply the same category and
subtype assertions after role-match hint decoration in
shortcuts/apps/apps_role_member_test.go (700-709), preserving the existing hint
checks.

In `@shortcuts/apps/apps_role.go`:
- Around line 431-448: Make role-list parsing fail closed in parseRoleListPage
by validating every item and requiring a usable role_id or id when processing
the unfiltered path; return an invalid-response error for malformed entries
rather than accepting them. In shortcuts/apps/apps_role_member.go lines 432-443,
reject present non-array roles fields and malformed role entries instead of
treating them as no matches. Add regression tests covering each malformed
response shape in both affected files.

In `@tests/cli_e2e/apps/coverage.md`:
- Line 46: Update the role-member-list row in the coverage table so the
member_type alternatives use the existing slash-separated convention instead of
literal pipe characters, preserving the meaning while keeping the row at the
expected column count.
🪄 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: 1cb84bc1-0ca8-461e-a529-4fbebb09e76d

📥 Commits

Reviewing files that changed from the base of the PR and between 37d490a and 09e87c8.

📒 Files selected for processing (14)
  • shortcuts/apps/apps_access_scope_get_test.go
  • shortcuts/apps/apps_role.go
  • shortcuts/apps/apps_role_common.go
  • shortcuts/apps/apps_role_common_test.go
  • shortcuts/apps/apps_role_member.go
  • shortcuts/apps/apps_role_member_test.go
  • shortcuts/apps/apps_role_test.go
  • shortcuts/apps/shortcuts.go
  • shortcuts/apps/shortcuts_test.go
  • skills/lark-apps/SKILL.md
  • skills/lark-apps/references/lark-apps-access-scope-set.md
  • skills/lark-apps/references/lark-apps-role.md
  • tests/cli_e2e/apps/apps_role_management_test.go
  • tests/cli_e2e/apps/coverage.md

Comment on lines +275 to +278
total := len(groups.Users) + len(groups.Departments) + len(groups.Chats)
if total == 0 {
return groups, appsValidationParamError("--users", "at least one of --users, --departments, or --chats is required").
WithHint("resolve names to IDs first, then pass --users open_id, --departments open_department_id, or --chats open_chat_id")

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 | 🟡 Minor | ⚡ Quick win

Avoid misleading single-flag metadata for multi-flag constraints.

  • shortcuts/apps/apps_role_common.go#L275-L278: omit param; any member flag can satisfy the constraint.
  • shortcuts/apps/apps_role.go#L193-L195: omit param; either update field is valid.
  • shortcuts/apps/apps_role_member.go#L370-L372: omit param; member flags or --all are valid alternatives.

As per coding guidelines, the param field must only name the user input that actually failed.

📍 Affects 3 files
  • shortcuts/apps/apps_role_common.go#L275-L278 (this comment)
  • shortcuts/apps/apps_role.go#L193-L195
  • shortcuts/apps/apps_role_member.go#L370-L372
🤖 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/apps/apps_role_common.go` around lines 275 - 278, Remove the
single-flag param metadata from the multi-flag validation errors: in
shortcuts/apps/apps_role_common.go lines 275-278, update the validation error
for the total users/departments/chats check; in shortcuts/apps/apps_role.go
lines 193-195, update the validation error for the alternative update fields;
and in shortcuts/apps/apps_role_member.go lines 370-372, update the validation
error for member flags or --all. Each error should omit param while preserving
its message and hint.

Source: Coding guidelines

Comment thread shortcuts/apps/apps_role_test.go
Comment thread shortcuts/apps/apps_role.go Outdated
Comment on lines +431 to +448
func parseRoleListPage(data map[string]interface{}) ([]interface{}, bool, error) {
rawItems, hasItems := data["items"]
items, ok := rawItems.([]interface{})
if !hasItems || !ok {
return nil, false, errs.NewInternalError(
errs.SubtypeInvalidResponse,
"role list response field items must be an array",
).WithHint("retry the read; do not treat a missing or malformed role list as empty")
}
rawHasMore, hasHasMore := data["has_more"]
hasMore, ok := rawHasMore.(bool)
if !hasHasMore || !ok {
return nil, false, errs.NewInternalError(
errs.SubtypeInvalidResponse,
"role list response field has_more must be a boolean",
).WithHint("retry the read; pagination is incomplete without a valid has_more value")
}
return items, hasMore, nil

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

Fail closed on malformed backend role collections.

  • shortcuts/apps/apps_role.go#L431-L448: validate every role item and require a usable role_id/id on the unfiltered path.
  • shortcuts/apps/apps_role_member.go#L432-L443: reject present non-array roles fields and malformed role entries instead of normalizing them to no matches.

Add regression tests for each malformed shape. As per coding guidelines, every behavior change needs a test alongside it.

📍 Affects 2 files
  • shortcuts/apps/apps_role.go#L431-L448 (this comment)
  • shortcuts/apps/apps_role_member.go#L432-L443
🤖 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/apps/apps_role.go` around lines 431 - 448, Make role-list parsing
fail closed in parseRoleListPage by validating every item and requiring a usable
role_id or id when processing the unfiltered path; return an invalid-response
error for malformed entries rather than accepting them. In
shortcuts/apps/apps_role_member.go lines 432-443, reject present non-array roles
fields and malformed role entries instead of treating them as no matches. Add
regression tests covering each malformed response shape in both affected files.

Source: Coding guidelines

Comment thread tests/cli_e2e/apps/coverage.md Outdated
@codecov

codecov Bot commented Jul 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.72917% with 137 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.78%. Comparing base (1ab8530) to head (e1f0bdd).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/apps/apps_role.go 81.81% 47 Missing and 19 partials ⚠️
shortcuts/apps/apps_role_member.go 86.87% 27 Missing and 15 partials ⚠️
shortcuts/apps/apps_role_common.go 89.09% 20 Missing and 9 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1881      +/-   ##
==========================================
+ Coverage   74.58%   74.78%   +0.19%     
==========================================
  Files         863      881      +18     
  Lines       90123    92690    +2567     
==========================================
+ Hits        67220    69318    +2098     
- Misses      17696    18018     +322     
- Partials     5207     5354     +147     

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

@linchao5102 linchao5102 requested a review from liangshuo-1 as a code owner July 14, 2026 09:48

@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

🧹 Nitpick comments (1)
shortcuts/apps/apps_role_common.go (1)

452-456: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Magic number "100" duplicated instead of referencing maxRoleMembers.

roleMemberLimitParams's reason string (L475) hardcodes "...exceeds 100", and the adjacent .WithHint(...) text (L455) hardcodes "at most 100 members". If maxRoleMembers ever changes, both strings silently go stale while the primary error message (which correctly uses %d, maxRoleMembers) stays accurate.

♻️ Proposed fix
-			WithHint("reduce the atomic request to at most 100 members; the CLI does not split member writes automatically")
+			WithHint(fmt.Sprintf("reduce the atomic request to at most %d members; the CLI does not split member writes automatically", maxRoleMembers))
 func roleMemberLimitParams(groups roleMemberGroups) []errs.InvalidParam {
-	reason := "combined role member count exceeds 100"
+	reason := fmt.Sprintf("combined role member count exceeds %d", maxRoleMembers)

Also applies to: 474-487

🤖 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/apps/apps_role_common.go` around lines 452 - 456, Replace the
hardcoded “100” values in the WithHint text near the maxRoleMembers check and
the reason string returned by roleMemberLimitParams with formatting that uses
maxRoleMembers, so all member-limit messages remain consistent when the constant
changes.
🤖 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_role_common.go`:
- Around line 288-313: The authorization branch in withRoleErrorHint currently
overwrites server-provided Problem.Hint details; preserve existing lifted detail
by appending the role-specific hint instead. Update the hint-selection logic so
CategoryAuthorization follows the same append behavior as other noncanonical
existing hints, while retaining the current handling for empty or canonical API
hints.

---

Nitpick comments:
In `@shortcuts/apps/apps_role_common.go`:
- Around line 452-456: Replace the hardcoded “100” values in the WithHint text
near the maxRoleMembers check and the reason string returned by
roleMemberLimitParams with formatting that uses maxRoleMembers, so all
member-limit messages remain consistent when the constant changes.
🪄 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: 93faca35-fe72-42ce-84cb-7ecac62e1859

📥 Commits

Reviewing files that changed from the base of the PR and between 09e87c8 and 7ec7b2f.

📒 Files selected for processing (13)
  • internal/errclass/codemeta_spark.go
  • internal/errclass/codemeta_spark_test.go
  • internal/qualitygate/rules/dryrun.go
  • internal/qualitygate/rules/dryrun_test.go
  • shortcuts/apps/apps_role.go
  • shortcuts/apps/apps_role_common.go
  • shortcuts/apps/apps_role_common_test.go
  • shortcuts/apps/apps_role_member.go
  • shortcuts/apps/apps_role_member_test.go
  • shortcuts/apps/apps_role_test.go
  • skills/lark-apps/references/lark-apps-role.md
  • tests/cli_e2e/apps/apps_role_management_test.go
  • tests/cli_e2e/apps/coverage.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • tests/cli_e2e/apps/coverage.md
  • shortcuts/apps/apps_role_test.go
  • shortcuts/apps/apps_role_member.go
  • skills/lark-apps/references/lark-apps-role.md
  • tests/cli_e2e/apps/apps_role_management_test.go
  • shortcuts/apps/apps_role_member_test.go

Comment on lines +288 to +313
// withRoleErrorHint refines documented Spark role errors with command-specific
// recovery while preserving the typed error, numeric code, log_id, and any
// server-provided detail. Unknown codes retain the existing Apps fallback.
func withRoleErrorHint(err error, operation roleErrorOperation) error {
if err == nil {
return nil
}
problem, ok := errs.ProblemOf(err)
if !ok {
return err
}
hint := roleErrorHint(problem.Code, operation)
if hint == "" {
return withAppsHint(err, roleFallbackHint(operation))
}

existing := strings.TrimSpace(problem.Hint)
canonicalAPIHint := strings.TrimSpace(errclass.APIHint(problem.Subtype))
switch {
case existing == "", existing == canonicalAPIHint, problem.Category == errs.CategoryAuthorization:
problem.Hint = hint
case !strings.Contains(existing, hint):
problem.Hint = existing + "; " + hint
}
return 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 | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check classification of role auth-related error codes and whether buildPermissionError can surface server detail.
rg -n -B2 -A10 '3344030|3344031' internal/errclass/codemeta_spark.go
rg -n -A30 'func buildPermissionError' internal/errclass/classify.go

Repository: larksuite/cli

Length of output: 3327


🏁 Script executed:

#!/bin/bash
sed -n '288,330p' shortcuts/apps/apps_role_common.go
printf '\n--- tests ---\n'
rg -n -A8 -B8 'withRoleErrorHint|PreservesServerDetail|roleErrAdminRequired|roleErrManagerRequired' shortcuts/apps -g '*test.go'

Repository: larksuite/cli

Length of output: 7140


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect BuildAPIError detail lifting and any auth-code detail tests/fixtures.
rg -n -A40 -B20 'func BuildAPIError|liftErrorDetailValues|details' internal/errclass -g '*.go'
printf '\n--- role auth detail fixtures/tests ---\n'
rg -n -A8 -B8 'roleErrAdminRequired|roleErrManagerRequired|3344030|3344031|PermissionDenied|CategoryAuthorization' shortcuts/apps internal/errclass -g '*test.go' -g '*.go'

Repository: larksuite/cli

Length of output: 50369


Authorization role errors overwrite server detail. BuildAPIError already lifts resp.error.details[].value into Problem.Hint, but this errs.CategoryAuthorization branch replaces that hint with the role-specific text for roleErrAdminRequired/roleErrManagerRequired. Preserve the lifted detail here and append the role hint instead.

🤖 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/apps/apps_role_common.go` around lines 288 - 313, The authorization
branch in withRoleErrorHint currently overwrites server-provided Problem.Hint
details; preserve existing lifted detail by appending the role-specific hint
instead. Update the hint-selection logic so CategoryAuthorization follows the
same append behavior as other noncanonical existing hints, while retaining the
current handling for empty or canonical API hints.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL Architecture-level or global-impact change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant