bf9395e022
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
165 lines
5.4 KiB
Go
165 lines
5.4 KiB
Go
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package okr
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
|
|
"github.com/larksuite/cli/errs"
|
|
"github.com/larksuite/cli/shortcuts/common"
|
|
)
|
|
|
|
// OKRListProgress lists progress for an objective or key result.
|
|
var OKRListProgress = common.Shortcut{
|
|
Service: "okr",
|
|
Command: "+progress-list",
|
|
Description: "List progress for an objective or key result",
|
|
Risk: "read",
|
|
Scopes: []string{"okr:okr.progress:readonly"},
|
|
AuthTypes: []string{"user", "bot"},
|
|
HasFormat: true,
|
|
Flags: []common.Flag{
|
|
{Name: "target-id", Desc: "target ID (objective or key result ID)", Required: true},
|
|
{Name: "target-type", Desc: "target type: objective | key_result", Required: true, Enum: []string{"objective", "key_result"}},
|
|
{Name: "user-id-type", Default: "open_id", Desc: "user ID type: open_id | union_id | user_id"},
|
|
{Name: "department-id-type", Default: "open_department_id", Desc: "department ID type: department_id | open_department_id"},
|
|
},
|
|
Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
|
|
targetID := runtime.Str("target-id")
|
|
if targetID == "" {
|
|
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--target-id is required").WithParam("--target-id")
|
|
}
|
|
if err := common.RejectDangerousCharsTyped("--target-id", targetID); err != nil {
|
|
return err
|
|
}
|
|
if id, err := strconv.ParseInt(targetID, 10, 64); err != nil || id <= 0 {
|
|
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--target-id must be a positive int64").WithParam("--target-id")
|
|
}
|
|
|
|
targetType := runtime.Str("target-type")
|
|
if _, ok := targetTypeAllowed[targetType]; !ok {
|
|
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--target-type must be one of: objective | key_result").WithParam("--target-type")
|
|
}
|
|
|
|
idType := runtime.Str("user-id-type")
|
|
if idType != "open_id" && idType != "union_id" && idType != "user_id" {
|
|
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--user-id-type must be one of: open_id | union_id | user_id").WithParam("--user-id-type")
|
|
}
|
|
|
|
deptIDType := runtime.Str("department-id-type")
|
|
if deptIDType != "department_id" && deptIDType != "open_department_id" {
|
|
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--department-id-type must be one of: department_id | open_department_id").WithParam("--department-id-type")
|
|
}
|
|
return nil
|
|
},
|
|
DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
|
|
targetID := runtime.Str("target-id")
|
|
targetType := runtime.Str("target-type")
|
|
params := map[string]interface{}{
|
|
"user_id_type": runtime.Str("user-id-type"),
|
|
"department_id_type": runtime.Str("department-id-type"),
|
|
"page_size": 100,
|
|
}
|
|
|
|
switch targetType {
|
|
case "objective":
|
|
return common.NewDryRunAPI().
|
|
GET("/open-apis/okr/v2/objectives/:objective_id/progresses").
|
|
Params(params).
|
|
Set("objective_id", targetID).
|
|
Desc("List progresses for objective")
|
|
case "key_result":
|
|
return common.NewDryRunAPI().
|
|
GET("/open-apis/okr/v2/key_results/:key_result_id/progresses").
|
|
Params(params).
|
|
Set("key_result_id", targetID).
|
|
Desc("List progresses for key result")
|
|
}
|
|
return nil
|
|
},
|
|
Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
|
|
targetID := runtime.Str("target-id")
|
|
targetType := runtime.Str("target-type")
|
|
userIDType := runtime.Str("user-id-type")
|
|
deptIDType := runtime.Str("department-id-type")
|
|
|
|
queryParams := map[string]interface{}{
|
|
"user_id_type": userIDType,
|
|
"department_id_type": deptIDType,
|
|
"page_size": "100",
|
|
}
|
|
|
|
var apiPath string
|
|
switch targetType {
|
|
case "objective":
|
|
apiPath = fmt.Sprintf("/open-apis/okr/v2/objectives/%s/progresses", targetID)
|
|
case "key_result":
|
|
apiPath = fmt.Sprintf("/open-apis/okr/v2/key_results/%s/progresses", targetID)
|
|
}
|
|
|
|
var allProgress []*Progress
|
|
for {
|
|
if err := ctx.Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
data, err := runtime.CallAPITyped("GET", apiPath, queryParams, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
itemsRaw, _ := data["items"].([]interface{})
|
|
for _, item := range itemsRaw {
|
|
raw, err := json.Marshal(item)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
var progress Progress
|
|
if err := json.Unmarshal(raw, &progress); err != nil {
|
|
continue
|
|
}
|
|
allProgress = append(allProgress, &progress)
|
|
}
|
|
|
|
hasMore, pageToken := common.PaginationMeta(data)
|
|
if !hasMore || pageToken == "" {
|
|
break
|
|
}
|
|
queryParams["page_token"] = pageToken
|
|
}
|
|
|
|
// Convert to response format
|
|
respProgress := make([]*RespProgress, 0, len(allProgress))
|
|
for _, p := range allProgress {
|
|
respProgress = append(respProgress, p.ToResp())
|
|
}
|
|
|
|
runtime.OutFormat(map[string]interface{}{
|
|
"progress_list": respProgress,
|
|
"total": len(respProgress),
|
|
}, nil, func(w io.Writer) {
|
|
fmt.Fprintf(w, "Found %d progress(es)\n", len(respProgress))
|
|
for _, p := range respProgress {
|
|
fmt.Fprintf(w, " [%s] , %s", p.ID, p.ModifyTime)
|
|
if p.ProgressRate != nil && p.ProgressRate.Percent != nil {
|
|
fmt.Fprintf(w, " (%.2f%%", *p.ProgressRate.Percent)
|
|
if p.ProgressRate.Status != nil {
|
|
fmt.Fprintf(w, ", %s", *p.ProgressRate.Status)
|
|
}
|
|
fmt.Fprintf(w, ")\n")
|
|
if p.Content != nil {
|
|
fmt.Fprintf(w, " Content: %s\n", *p.Content)
|
|
}
|
|
}
|
|
fmt.Fprintln(w)
|
|
}
|
|
})
|
|
return nil
|
|
},
|
|
}
|