From 3f844dbcab054c8cd7d7e7624696c02b1a511a44 Mon Sep 17 00:00:00 2001 From: fsafasff <2624934035@qq.com> Date: Tue, 26 May 2026 17:31:29 +0800 Subject: [PATCH] fix(issue): preserve metadata during updates --- shortcuts/issue/issue.go | 47 ++++++++++++++++++++++++++++++++++ shortcuts/issue/issue_test.go | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/shortcuts/issue/issue.go b/shortcuts/issue/issue.go index 902fda8..6f86252 100644 --- a/shortcuts/issue/issue.go +++ b/shortcuts/issue/issue.go @@ -18,6 +18,9 @@ func v1RepoPath(ctx *common.RuntimeContext) string { type existingIssue struct { Subject string Description string + StatusID interface{} + PriorityID interface{} + TagIDs []interface{} } func Shortcuts() []*common.Shortcut { @@ -134,6 +137,8 @@ func Shortcuts() []*common.Shortcut { "description": current.Description, "status_id": 5, // 5 = closed } + preserveIssueMetadata(body, current) + body["status_id"] = 5 env, err := ctx.CallAPI("PATCH", fmt.Sprintf("%s/issues/%s", v1RepoPath(ctx), number), body) if err != nil { return err @@ -174,6 +179,7 @@ func Shortcuts() []*common.Shortcut { "subject": current.Subject, "description": current.Description, } + preserveIssueMetadata(body, current) if t := ctx.Arg("title"); t != "" { body["subject"] = t } @@ -273,9 +279,50 @@ func fetchExistingIssue(ctx *common.RuntimeContext, number string) (*existingIss return &existingIssue{ Subject: subject, Description: description, + StatusID: nestedID(issueData, "status"), + PriorityID: nestedID(issueData, "priority"), + TagIDs: issueTagIDs(issueData), }, nil } +func preserveIssueMetadata(body map[string]interface{}, issue *existingIssue) { + if issue.StatusID != nil { + body["status_id"] = issue.StatusID + } + if issue.PriorityID != nil { + body["priority_id"] = issue.PriorityID + } + if len(issue.TagIDs) > 0 { + body["issue_tag_ids"] = issue.TagIDs + } +} + +func nestedID(data map[string]interface{}, key string) interface{} { + item, ok := data[key].(map[string]interface{}) + if !ok { + return nil + } + return item["id"] +} + +func issueTagIDs(data map[string]interface{}) []interface{} { + tags, ok := data["tags"].([]interface{}) + if !ok { + return nil + } + ids := make([]interface{}, 0, len(tags)) + for _, item := range tags { + tag, ok := item.(map[string]interface{}) + if !ok { + continue + } + if id, ok := tag["id"]; ok { + ids = append(ids, id) + } + } + return ids +} + func normalizeIssueStatus(state string) (interface{}, error) { switch strings.ToLower(strings.TrimSpace(state)) { case "open": diff --git a/shortcuts/issue/issue_test.go b/shortcuts/issue/issue_test.go index 088f82c..83a1cf7 100644 --- a/shortcuts/issue/issue_test.go +++ b/shortcuts/issue/issue_test.go @@ -100,6 +100,54 @@ func TestIssueUpdatePreservesCurrentSubjectWhenChangingDescription(t *testing.T) assertEqual(t, updatePayload["description"], "New description") } +func TestIssueUpdatePreservesCurrentMetadata(t *testing.T) { + var updatePayload map[string]interface{} + server := newIssueTestServer(t, func(w http.ResponseWriter, r *http.Request) { + switch { + case r.Method == "GET" && r.URL.Path == "/v1/owner/repo/issues/42.json": + writeJSON(t, w, map[string]interface{}{ + "subject": "Existing title", + "description": "Existing description", + "status": map[string]interface{}{ + "id": float64(1), + }, + "priority": map[string]interface{}{ + "id": float64(2), + }, + "tags": []interface{}{ + map[string]interface{}{"id": float64(7)}, + map[string]interface{}{"id": float64(8)}, + }, + }) + case r.Method == "PATCH" && r.URL.Path == "/v1/owner/repo/issues/42.json": + updatePayload = decodeJSON(t, r) + writeJSON(t, w, updatePayload) + default: + t.Fatalf("unexpected request: %s %s", r.Method, r.URL.Path) + } + }) + defer server.Close() + + err := runIssueShortcut(t, server, "update", map[string]string{ + "number": "42", + "title": "New title", + }) + if err != nil { + t.Fatalf("update shortcut failed: %v", err) + } + + assertEqual(t, updatePayload["subject"], "New title") + assertEqual(t, updatePayload["description"], "Existing description") + assertEqual(t, updatePayload["status_id"], float64(1)) + assertEqual(t, updatePayload["priority_id"], float64(2)) + tagIDs, ok := updatePayload["issue_tag_ids"].([]interface{}) + if !ok { + t.Fatalf("issue_tag_ids missing or wrong type: %T", updatePayload["issue_tag_ids"]) + } + assertEqual(t, tagIDs[0], float64(7)) + assertEqual(t, tagIDs[1], float64(8)) +} + func TestBatchClosePreservesCurrentDescription(t *testing.T) { var updatePayload map[string]interface{} server := newIssueTestServer(t, func(w http.ResponseWriter, r *http.Request) {