mirror of
https://github.com/larksuite/cli.git
synced 2026-07-03 14:02:43 +08:00
feat(im): add recovery hint for cross-identity message resources (#652)
Change-Id: I8a43486333638271f0fbbcffca81a60c9f9d2060
This commit is contained in:
committed by
GitHub
parent
fc9f9c1f26
commit
0bbd0f2c7d
@@ -49,6 +49,9 @@ const (
|
||||
// caller already holds the requested permission, or the target type does
|
||||
// not accept apply operations).
|
||||
LarkErrDrivePermApplyNotApplicable = 1063007
|
||||
|
||||
// IM resource ownership mismatch.
|
||||
LarkErrOwnershipMismatch = 231205
|
||||
)
|
||||
|
||||
// ClassifyLarkError maps a Lark API error code + message to (exitCode, errType, hint).
|
||||
@@ -98,6 +101,9 @@ func ClassifyLarkError(code int, msg string) (int, string, string) {
|
||||
case LarkErrDrivePermApplyNotApplicable:
|
||||
return ExitAPI, "invalid_params",
|
||||
"this document does not accept a permission-apply request (common causes: the document is configured to disallow access requests, the caller already holds the permission, or the target type does not support apply); contact the owner directly"
|
||||
|
||||
case LarkErrOwnershipMismatch:
|
||||
return ExitAPI, "ownership_mismatch", buildOwnershipRecoveryHint()
|
||||
}
|
||||
|
||||
return ExitAPI, "api_error", ""
|
||||
|
||||
@@ -61,6 +61,13 @@ func TestClassifyLarkError_DriveCreateShortcutConstraints(t *testing.T) {
|
||||
wantType: "invalid_params",
|
||||
wantHint: "does not accept a permission-apply request",
|
||||
},
|
||||
{
|
||||
name: "ownership mismatch",
|
||||
code: LarkErrOwnershipMismatch,
|
||||
wantExitCode: ExitAPI,
|
||||
wantType: "ownership_mismatch",
|
||||
wantHint: "messages-resources-download",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
8
internal/output/ownership_recovery.go
Normal file
8
internal/output/ownership_recovery.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package output
|
||||
|
||||
func buildOwnershipRecoveryHint() string {
|
||||
return "This resource belongs to another user — you can't send it directly. Download it with 'im +messages-resources-download --output <output_path>', then send the local file via 'im +send..'. For post or interactive, upload first and use the new image_key or file_key."
|
||||
}
|
||||
71
internal/output/ownership_recovery_test.go
Normal file
71
internal/output/ownership_recovery_test.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package output
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func checkOwnershipRecoveryHint(t *testing.T, hint string) {
|
||||
t.Helper()
|
||||
|
||||
for _, part := range []string{
|
||||
"im +messages-resources-download",
|
||||
"--output <output_path>",
|
||||
"This resource belongs to another user",
|
||||
"download",
|
||||
"send",
|
||||
"image_key",
|
||||
"file_key",
|
||||
} {
|
||||
if !strings.Contains(hint, part) {
|
||||
t.Fatalf("hint %q missing %q", hint, part)
|
||||
}
|
||||
}
|
||||
if len(hint) > 360 {
|
||||
t.Fatalf("hint is too long: %d bytes", len(hint))
|
||||
}
|
||||
for _, noisy := range []string{
|
||||
"Step 1",
|
||||
"Step 2",
|
||||
"Step 3",
|
||||
"--message-id <message_id>",
|
||||
"--file-key <resource_key>",
|
||||
"--type <image|file>",
|
||||
"identity",
|
||||
"do not keep retrying alternative download methods",
|
||||
"POST /open-apis",
|
||||
} {
|
||||
if strings.Contains(hint, noisy) {
|
||||
t.Fatalf("hint %q should not contain noisy phrase %q", hint, noisy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildOwnershipRecoveryHint(t *testing.T) {
|
||||
checkOwnershipRecoveryHint(t, buildOwnershipRecoveryHint())
|
||||
}
|
||||
|
||||
func TestErrAPI_OwnershipMismatch(t *testing.T) {
|
||||
upstreamMessage := "Bot or User is NOT the owner of the uat resource."
|
||||
err := ErrAPI(LarkErrOwnershipMismatch, upstreamMessage, map[string]any{"log_id": "test-log"})
|
||||
|
||||
if err.Code != ExitAPI {
|
||||
t.Fatalf("exit code = %d, want %d", err.Code, ExitAPI)
|
||||
}
|
||||
if err.Detail == nil {
|
||||
t.Fatal("expected detail")
|
||||
}
|
||||
if err.Detail.Type != "ownership_mismatch" {
|
||||
t.Fatalf("type = %q, want %q", err.Detail.Type, "ownership_mismatch")
|
||||
}
|
||||
if got, want := err.Detail.Message, upstreamMessage; got != want {
|
||||
t.Fatalf("message = %q, want %q", got, want)
|
||||
}
|
||||
checkOwnershipRecoveryHint(t, err.Detail.Hint)
|
||||
if err.Detail.Detail == nil {
|
||||
t.Fatal("expected upstream detail to be preserved")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user