// 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" ) // OKRGetProgressRecord gets a progress by ID. var OKRGetProgressRecord = common.Shortcut{ Service: "okr", Command: "+progress-get", Description: "Get an OKR progress by ID", Risk: "read", Scopes: []string{"okr:okr.progress:readonly"}, AuthTypes: []string{"user", "bot"}, HasFormat: true, Flags: []common.Flag{ {Name: "progress-id", Desc: "progress ID (int64)", Required: true}, {Name: "user-id-type", Default: "open_id", Desc: "user ID type: open_id | union_id | user_id"}, {Name: "style", Default: "simple", Desc: "output style: simple (semi-plain text JSON) | richtext (ContentBlock JSON)", Enum: []string{"simple", "richtext"}}, }, Validate: func(ctx context.Context, runtime *common.RuntimeContext) error { progressID := runtime.Str("progress-id") if progressID == "" { return errs.NewValidationError(errs.SubtypeInvalidArgument, "--progress-id is required").WithParam("--progress-id") } if id, err := strconv.ParseInt(progressID, 10, 64); err != nil || id <= 0 { return errs.NewValidationError(errs.SubtypeInvalidArgument, "--progress-id must be a positive int64").WithParam("--progress-id") } 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") } style := runtime.Str("style") if style != "simple" && style != "richtext" { return errs.NewValidationError(errs.SubtypeInvalidArgument, "--style must be one of: simple | richtext").WithParam("--style") } return nil }, DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI { progressID := runtime.Str("progress-id") params := map[string]interface{}{ "user_id_type": runtime.Str("user-id-type"), } return common.NewDryRunAPI(). GET("/open-apis/okr/v1/progress_records/:progress_id"). Params(params). Set("progress_id", progressID). Desc("Get OKR progress") }, Execute: func(ctx context.Context, runtime *common.RuntimeContext) error { progressID := runtime.Str("progress-id") userIDType := runtime.Str("user-id-type") style := runtime.Str("style") queryParams := map[string]interface{}{"user_id_type": userIDType} path := fmt.Sprintf("/open-apis/okr/v1/progress_records/%s", progressID) data, err := runtime.CallAPITyped("GET", path, queryParams, nil) if err != nil { return err } record, err := parseProgressRecord(data) if err != nil { return err } var result map[string]interface{} if style == "simple" { resp := record.ToSimple() result = map[string]interface{}{ "progress": resp, "style": style, } runtime.OutFormat(result, nil, func(w io.Writer) { fmt.Fprintf(w, "Progress [%s] (style: %s)\n", resp.ID, style) fmt.Fprintf(w, " ModifyTime: %s\n", resp.ModifyTime) if resp.ProgressRate != nil && resp.ProgressRate.Percent != nil { fmt.Fprintf(w, " ProgressRate: %.1f%%\n", *resp.ProgressRate.Percent) } if resp.Content != nil { fmt.Fprintf(w, " Content: %s\n", resp.Content.Text) if len(resp.Content.Mention) > 0 { fmt.Fprintf(w, " Mentions: %v\n", resp.Content.Mention) } } }) } else { resp := record.ToResp() result = map[string]interface{}{ "progress": resp, "style": style, } runtime.OutFormat(result, nil, func(w io.Writer) { fmt.Fprintf(w, "Progress [%s] (style: %s)\n", resp.ID, style) fmt.Fprintf(w, " ModifyTime: %s\n", resp.ModifyTime) if resp.ProgressRate != nil && resp.ProgressRate.Percent != nil { fmt.Fprintf(w, " ProgressRate: %.1f%%\n", *resp.ProgressRate.Percent) } if resp.Content != nil { fmt.Fprintf(w, " Content: %s\n", *resp.Content) } }) } return nil }, } // parseProgressRecord parses a single progress from API response data. func parseProgressRecord(data map[string]any) (*ProgressV1, error) { raw, err := json.Marshal(data) if err != nil { return nil, errs.NewInternalError(errs.SubtypeInvalidResponse, "invalid progress response: marshal failed: %s", err).WithCause(err) } var record ProgressV1 if err := json.Unmarshal(raw, &record); err != nil { return nil, errs.NewInternalError(errs.SubtypeInvalidResponse, "invalid progress response: unmarshal failed: %s", err).WithCause(err) } return &record, nil }