项目文件夹

文件
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:22:54 +08:00

179 行
5.9 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package okr
import (
"context"
"fmt"
"io"
"math"
"strconv"
"github.com/larksuite/cli/errs"
"github.com/larksuite/cli/shortcuts/common"
)
// parseIndicatorValue parses and validates the indicator value.
func parseIndicatorValue(valueStr string) (float64, error) {
value, err := strconv.ParseFloat(valueStr, 64)
if err != nil {
return 0, errs.NewValidationError(errs.SubtypeInvalidArgument, "--value must be a number between -99999999999 and 99999999999").WithParam("--value").WithCause(err)
}
if math.IsNaN(value) || math.IsInf(value, 0) || value < -99999999999 || value > 99999999999 {
return 0, errs.NewValidationError(errs.SubtypeInvalidArgument, "--value must be a number between -99999999999 and 99999999999").WithParam("--value")
}
return value, nil
}
// fetchIndicatorID fetches the indicator ID for an objective or key result.
// The indicators.list API returns a single indicator object (not a list),
// which always exists (may be a default empty indicator).
func fetchIndicatorID(ctx context.Context, runtime *common.RuntimeContext, level string, id string) (string, error) {
var path string
var params map[string]interface{}
if level == "objective" {
path = fmt.Sprintf("/open-apis/okr/v2/objectives/%s/indicators", id)
params = map[string]interface{}{"page_size": 100}
} else {
path = fmt.Sprintf("/open-apis/okr/v2/key_results/%s/indicators", id)
params = map[string]interface{}{"page_size": 100}
}
data, err := runtime.CallAPITyped("GET", path, params, nil)
if err != nil {
return "", wrapOkrNetworkErr(err, "failed to fetch indicators")
}
// Parse response to get indicator ID
// Response format: {"indicator": {"id": "...", ...}} (single object, not a list)
indicator, ok := data["indicator"].(map[string]interface{})
if !ok {
return "", errs.NewInternalError(errs.SubtypeUnknown, "indicator field not found in response")
}
indicatorID, ok := indicator["id"].(string)
if !ok || indicatorID == "" {
return "", errs.NewInternalError(errs.SubtypeUnknown, "indicator ID not found or empty")
}
return indicatorID, nil
}
// OKRIndicatorUpdate updates the current value of an indicator for an objective or key result.
var OKRIndicatorUpdate = common.Shortcut{
Service: "okr",
Command: "+indicator-update",
Description: "Update the indicator current value for an objective or key result",
Risk: "write",
Scopes: []string{"okr:okr.content:writeonly"},
AuthTypes: []string{"user", "bot"},
HasFormat: true,
Flags: []common.Flag{
{Name: "level", Desc: "level to update: objective | key-result, Required.", Enum: []string{"objective", "key-result"}},
{Name: "id", Desc: "objective or key result ID (int64), Required."},
{Name: "value", Desc: "new current value for the indicator (number), Required."},
},
Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
level := runtime.Str("level")
if level == "" {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--level is required").WithParam("--level")
}
if level != "objective" && level != "key-result" {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--level must be one of: objective | key-result").WithParam("--level")
}
id := runtime.Str("id")
if id == "" {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--id is required").WithParam("--id")
}
if _, err := strconv.ParseInt(id, 10, 64); err != nil {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--id must be a valid int64").WithParam("--id")
}
if runtime.Str("value") == "" {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--value is required").WithParam("--value")
}
if _, err := parseIndicatorValue(runtime.Str("value")); err != nil {
return err
}
return nil
},
DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
level := runtime.Str("level")
id := runtime.Str("id")
value, _ := parseIndicatorValue(runtime.Str("value"))
apis := common.NewDryRunAPI()
var listPath string
if level == "objective" {
listPath = fmt.Sprintf("/open-apis/okr/v2/objectives/%s/indicators", id)
} else {
listPath = fmt.Sprintf("/open-apis/okr/v2/key_results/%s/indicators", id)
}
// First API: fetch indicator list
apis = apis.
GET(listPath).
Params(map[string]interface{}{"page_size": 100}).
Desc(fmt.Sprintf("Fetch indicators for the %s to get indicator ID", level))
// Second API: patch indicator value
patchPath := "/open-apis/okr/v2/indicators/:indicator_id"
patchBody := map[string]interface{}{
"current_value": value,
}
apis = apis.
PATCH(patchPath).
Body(patchBody).
Set("indicator_id", "<indicator_id_from_list>").
Desc("Update indicator current value")
return apis
},
Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
level := runtime.Str("level")
id := runtime.Str("id")
value, err := parseIndicatorValue(runtime.Str("value"))
if err != nil {
return err
}
// Step 1: Fetch indicator ID
indicatorID, err := fetchIndicatorID(ctx, runtime, level, id)
if err != nil {
return err
}
// Step 2: Update indicator value
patchPath := fmt.Sprintf("/open-apis/okr/v2/indicators/%s", indicatorID)
patchBody := map[string]interface{}{
"current_value": value,
}
_, err = runtime.CallAPITyped("PATCH", patchPath, nil, patchBody)
if err != nil {
return wrapOkrNetworkErr(err, "failed to update indicator value")
}
// Build response
result := map[string]interface{}{
"indicator_id": indicatorID,
"current_value": value,
"level": level,
"target_id": id,
}
runtime.OutFormat(result, nil, func(w io.Writer) {
fmt.Fprintf(w, "Updated Indicator [%s]\n", indicatorID)
fmt.Fprintf(w, " Level: %s\n", level)
fmt.Fprintf(w, " Target ID: %s\n", id)
fmt.Fprintf(w, " Current Value: %v\n", value)
})
return nil
},
}