Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion shortcuts/contact/contact_search_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ type searchUserAPIData struct {
Items []searchUserAPIItem `json:"items"`
HasMore bool `json:"has_more"`
PageToken string `json:"page_token"`
Notice string `json:"notice"`
}

type searchUserAPIItem struct {
Expand Down Expand Up @@ -126,6 +127,7 @@ type searchUser struct {
type searchUserResponse struct {
Users []searchUser `json:"users"`
HasMore bool `json:"has_more"`
Notice string `json:"notice,omitempty"`
}

var ContactSearchUser = common.Shortcut{
Expand Down Expand Up @@ -222,7 +224,7 @@ func executeSearchUserSingle(ctx context.Context, runtime *common.RuntimeContext
}

users, hasMore := projectUsers(respData, runtime.Str("lang"), runtime.Config.Brand)
out := searchUserResponse{Users: users, HasMore: hasMore}
out := searchUserResponse{Users: users, HasMore: hasMore, Notice: respData.Notice}

runtime.OutFormat(out, &output.Meta{Count: len(users)}, func(w io.Writer) {
if len(users) == 0 {
Expand Down
9 changes: 8 additions & 1 deletion shortcuts/contact/contact_search_user_fanout.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type fanoutResult struct {
Query string
Users []searchUser
HasMore bool
Notice string
ErrMsg string // empty = success
Err error // original failure, kept for typed all-failed propagation
}
Expand Down Expand Up @@ -94,7 +95,7 @@ func runOneQuery(ctx context.Context, runtime *common.RuntimeContext, index int,
}

users, hasMore := projectUsers(respData, runtime.Str("lang"), runtime.Config.Brand)
return fanoutResult{Index: index, Query: query, Users: users, HasMore: hasMore}
return fanoutResult{Index: index, Query: query, Users: users, HasMore: hasMore, Notice: respData.Notice}
}

func fanoutErrorResult(index int, query string, err error) fanoutResult {
Expand All @@ -113,11 +114,13 @@ type querySummary struct {
Query string `json:"query"`
Error string `json:"error,omitempty"`
HasMore bool `json:"has_more"`
Notice string `json:"notice,omitempty"`
}

type fanoutResponse struct {
Users []fanoutUser `json:"users"`
Queries []querySummary `json:"queries"`
Notice string `json:"notice,omitempty"`
}

// buildFanoutResponse walks results by Index (input order), flattens users[]
Expand All @@ -142,6 +145,7 @@ func buildFanoutResponse(queries []string, results []fanoutResult) (*fanoutRespo
Query: queries[i],
Error: r.ErrMsg,
HasMore: r.HasMore,
Notice: r.Notice,
})
if r.ErrMsg != "" {
failed++
Expand All @@ -152,6 +156,9 @@ func buildFanoutResponse(queries []string, results []fanoutResult) (*fanoutRespo
}
continue
}
if out.Notice == "" {
out.Notice = r.Notice
}
for _, u := range r.Users {
out.Users = append(out.Users, fanoutUser{searchUser: u, MatchedQuery: queries[i]})
}
Expand Down
12 changes: 12 additions & 0 deletions shortcuts/contact/contact_search_user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ func searchUserStub() *httpmock.Stub {
Body: map[string]interface{}{
"code": 0, "msg": "ok",
"data": map[string]interface{}{
"notice": "The query is too long and has been truncated to the first 50 characters for search.",
"items": []interface{}{
map[string]interface{}{
"id": "ou_a",
Expand Down Expand Up @@ -631,6 +632,9 @@ func TestSearchUser_Integration_JSONStructuredFields(t *testing.T) {
if !ok {
t.Fatalf("envelope.data: expected object, got %v\nraw=%s", got["data"], stdout.String())
}
if data["notice"] != "The query is too long and has been truncated to the first 50 characters for search." {
t.Fatalf("data.notice = %v", data["notice"])
}
users, _ := data["users"].([]interface{})
if len(users) != 1 {
t.Fatalf("users: expected 1, got %d (output=%s)", len(users), stdout.String())
Expand Down Expand Up @@ -1406,6 +1410,7 @@ func TestFanout_PartialFailure_ExitZero(t *testing.T) {
BodyFilter: func(b []byte) bool { return strings.Contains(string(b), `"alice"`) },
Body: map[string]interface{}{"code": 0, "msg": "ok",
"data": map[string]interface{}{
"notice": "The query is too long and has been truncated to the first 50 characters for search.",
"items": []interface{}{map[string]interface{}{"id": "ou_a"}},
"has_more": false,
}},
Expand All @@ -1432,10 +1437,17 @@ func TestFanout_PartialFailure_ExitZero(t *testing.T) {
if len(users) != 1 {
t.Errorf("users: expected 1 (alice), got %d; stdout=%s", len(users), stdout.String())
}
if data["notice"] != "The query is too long and has been truncated to the first 50 characters for search." {
t.Fatalf("data.notice = %v", data["notice"])
}
queries := data["queries"].([]interface{})
if len(queries) != 2 {
t.Fatalf("queries: expected 2, got %d", len(queries))
}
q0 := queries[0].(map[string]interface{})
if q0["notice"] != "The query is too long and has been truncated to the first 50 characters for search." {
t.Fatalf("queries[0].notice = %v", q0["notice"])
}
q1 := queries[1].(map[string]interface{})
if !strings.HasPrefix(q1["error"].(string), "HTTP 500") {
t.Errorf("queries[1].error: got %q", q1["error"])
Expand Down
3 changes: 3 additions & 0 deletions shortcuts/doc/docs_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ var DocsSearch = common.Shortcut{
"page_token": data["page_token"],
"results": normalizedItems,
}
if notice, _ := data["notice"].(string); notice != "" {
resultData["notice"] = notice
}

runtime.OutFormat(resultData, &output.Meta{Count: len(normalizedItems)}, func(w io.Writer) {
if len(normalizedItems) == 0 {
Expand Down
38 changes: 38 additions & 0 deletions shortcuts/doc/docs_search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,46 @@ import (
"encoding/json"
"strings"
"testing"

"github.com/larksuite/cli/internal/cmdutil"
"github.com/larksuite/cli/internal/httpmock"
)

func TestDocsSearchExecutePassesThroughNotice(t *testing.T) {
const notice = "The query is too long and has been truncated to the first 50 characters for search."

f, stdout, _, reg := cmdutil.TestFactory(t, docsTestConfigWithAppID("docs-search-notice"))
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/search/v2/doc_wiki/search",
Body: map[string]interface{}{
"code": 0,
"msg": "ok",
"data": map[string]interface{}{
"notice": notice,
"res_units": []interface{}{},
"total": 0,
"has_more": false,
"page_token": "",
},
},
})

if err := mountAndRunDocs(t, DocsSearch, []string{"+search", "--query", "incident", "--format", "json", "--as", "user"}, f, stdout); err != nil {
t.Fatalf("DocsSearch.Execute() error = %v", err)
}
reg.Verify(t)

var env map[string]interface{}
if err := json.Unmarshal(stdout.Bytes(), &env); err != nil {
t.Fatalf("json.Unmarshal(stdout) error = %v\nstdout=%s", err, stdout.String())
}
data, _ := env["data"].(map[string]interface{})
if got, _ := data["notice"].(string); got != notice {
t.Fatalf("data.notice = %q, want %q; data=%#v", got, notice, data)
}
}

func TestAddIsoTimeFieldsSupportsJSONNumber(t *testing.T) {
t.Parallel()

Expand Down
3 changes: 3 additions & 0 deletions shortcuts/drive/drive_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ var DriveSearch = common.Shortcut{
"page_token": data["page_token"],
"results": normalizedItems,
}
if notice, _ := data["notice"].(string); notice != "" {
resultData["notice"] = notice
}

runtime.OutFormat(resultData, &output.Meta{Count: len(normalizedItems)}, func(w io.Writer) {
renderDriveSearchTable(w, data, normalizedItems)
Expand Down
37 changes: 37 additions & 0 deletions shortcuts/drive/drive_search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,47 @@ import (
"time"

"github.com/larksuite/cli/errs"
"github.com/larksuite/cli/internal/cmdutil"
"github.com/larksuite/cli/internal/errclass"
"github.com/larksuite/cli/internal/httpmock"
"github.com/larksuite/cli/internal/output"
)

func TestDriveSearchExecutePassesThroughNotice(t *testing.T) {
const notice = "The query is too long and has been truncated to the first 50 characters for search."

f, stdout, _, reg := cmdutil.TestFactory(t, driveTestConfig())
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/search/v2/doc_wiki/search",
Body: map[string]interface{}{
"code": 0,
"msg": "ok",
"data": map[string]interface{}{
"notice": notice,
"res_units": []interface{}{},
"total": 0,
"has_more": false,
"page_token": "",
},
},
})

if err := mountAndRunDrive(t, DriveSearch, []string{"+search", "--query", "incident", "--format", "json", "--as", "user"}, f, stdout); err != nil {
t.Fatalf("DriveSearch.Execute() error = %v", err)
}
reg.Verify(t)

var env map[string]interface{}
if err := json.Unmarshal(stdout.Bytes(), &env); err != nil {
t.Fatalf("json.Unmarshal(stdout) error = %v\nstdout=%s", err, stdout.String())
}
data, _ := env["data"].(map[string]interface{})
if got, _ := data["notice"].(string); got != notice {
t.Fatalf("data.notice = %q, want %q; data=%#v", got, notice, data)
}
}

// TestClampOpenedTimeWindow covers the 3-month / 1-year boundary logic that
// narrows --opened-since / --opened-until and generates the multi-slice notice.
func TestClampOpenedTimeWindow(t *testing.T) {
Expand Down
3 changes: 3 additions & 0 deletions shortcuts/im/im_chat_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ var ImChatSearch = common.Shortcut{
"has_more": hasMore,
"page_token": pageToken,
}
if notice, _ := resData["notice"].(string); notice != "" {
outData["notice"] = notice
}
if mfOut.Meta.Applied != "" {
outData["filter"] = MuteFilterMetaToMap(mfOut.Meta)
}
Expand Down
21 changes: 17 additions & 4 deletions shortcuts/im/im_messages_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ var ImMessagesSearch = common.Shortcut{
return err
}

rawItems, hasMore, nextPageToken, truncatedByLimit, pageLimit, err := searchMessages(runtime, req)
rawItems, hasMore, nextPageToken, truncatedByLimit, pageLimit, notice, err := searchMessages(runtime, req)
if err != nil {
return err
}
Expand All @@ -103,6 +103,9 @@ var ImMessagesSearch = common.Shortcut{
"has_more": hasMore,
"page_token": nextPageToken,
}
if notice != "" {
outData["notice"] = notice
}
runtime.OutFormat(outData, nil, func(w io.Writer) {
fmt.Fprintln(w, "No matching messages found.")
})
Expand Down Expand Up @@ -131,6 +134,9 @@ var ImMessagesSearch = common.Shortcut{
"page_token": nextPageToken,
"note": "failed to fetch message details, returning ID list only",
}
if notice != "" {
outData["notice"] = notice
}
runtime.OutFormat(outData, nil, func(w io.Writer) {
fmt.Fprintf(w, "Found %d messages (failed to fetch details):\n", len(messageIds))
for _, id := range messageIds {
Expand Down Expand Up @@ -206,6 +212,9 @@ var ImMessagesSearch = common.Shortcut{
"has_more": hasMore,
"page_token": nextPageToken,
}
if notice != "" {
outData["notice"] = notice
}
runtime.OutFormat(outData, nil, func(w io.Writer) {
if len(enriched) == 0 {
fmt.Fprintln(w, "No matching messages found.")
Expand Down Expand Up @@ -392,7 +401,7 @@ func messagesSearchPaginationConfig(runtime *common.RuntimeContext) (autoPaginat
return autoPaginate, pageLimit
}

func searchMessages(runtime *common.RuntimeContext, req *messagesSearchRequest) ([]interface{}, bool, string, bool, int, error) {
func searchMessages(runtime *common.RuntimeContext, req *messagesSearchRequest) ([]interface{}, bool, string, bool, int, string, error) {
autoPaginate, pageLimit := messagesSearchPaginationConfig(runtime)
pageToken := ""
if tokens := req.params["page_token"]; len(tokens) > 0 {
Expand All @@ -410,6 +419,7 @@ func searchMessages(runtime *common.RuntimeContext, req *messagesSearchRequest)
lastPageToken string
truncatedByLimit bool
pageCount int
notice string
)

for {
Expand All @@ -423,9 +433,12 @@ func searchMessages(runtime *common.RuntimeContext, req *messagesSearchRequest)

searchData, err := runtime.DoAPIJSONTyped(http.MethodPost, "/open-apis/im/v1/messages/search", params, req.body)
if err != nil {
return nil, false, "", false, pageLimit, err
return nil, false, "", false, pageLimit, "", err
}

if notice == "" {
notice, _ = searchData["notice"].(string)
}
items, _ := searchData["items"].([]interface{})
allItems = append(allItems, items...)
lastHasMore, lastPageToken = common.PaginationMeta(searchData)
Expand All @@ -441,7 +454,7 @@ func searchMessages(runtime *common.RuntimeContext, req *messagesSearchRequest)
pageToken = lastPageToken
}

return allItems, lastHasMore, lastPageToken, truncatedByLimit, pageLimit, nil
return allItems, lastHasMore, lastPageToken, truncatedByLimit, pageLimit, notice, nil
}

func batchMGetMessages(runtime *common.RuntimeContext, messageIds []string) ([]interface{}, error) {
Expand Down
Loading
Loading