fix: preserve issue metadata on update
This commit is contained in:
parent
2f8b987c2c
commit
ef79293956
|
|
@ -18,6 +18,7 @@ func v1RepoPath(ctx *common.RuntimeContext) string {
|
|||
type existingIssue struct {
|
||||
Subject string
|
||||
Description string
|
||||
Metadata map[string]interface{}
|
||||
}
|
||||
|
||||
func Shortcuts() []*common.Shortcut {
|
||||
|
|
@ -134,6 +135,7 @@ func Shortcuts() []*common.Shortcut {
|
|||
"description": current.Description,
|
||||
"status_id": 5, // 5 = closed
|
||||
}
|
||||
copyIssueMetadata(body, current.Metadata)
|
||||
env, err := ctx.CallAPI("PATCH", fmt.Sprintf("%s/issues/%s", v1RepoPath(ctx), number), body)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -174,6 +176,7 @@ func Shortcuts() []*common.Shortcut {
|
|||
"subject": current.Subject,
|
||||
"description": current.Description,
|
||||
}
|
||||
copyIssueMetadata(body, current.Metadata)
|
||||
if t := ctx.Arg("title"); t != "" {
|
||||
body["subject"] = t
|
||||
}
|
||||
|
|
@ -315,9 +318,90 @@ func fetchExistingIssue(ctx *common.RuntimeContext, number string) (*existingIss
|
|||
return &existingIssue{
|
||||
Subject: subject,
|
||||
Description: description,
|
||||
Metadata: existingIssueMetadata(issueData),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func existingIssueMetadata(issueData map[string]interface{}) map[string]interface{} {
|
||||
metadata := map[string]interface{}{}
|
||||
copyIDValue(metadata, "priority_id", issueData["priority_id"])
|
||||
copyNestedIDValue(metadata, "priority_id", issueData["priority"])
|
||||
copyIDValue(metadata, "tracker_id", issueData["tracker_id"])
|
||||
copyNestedIDValue(metadata, "tracker_id", issueData["tracker"])
|
||||
copyIDValue(metadata, "fixed_version_id", issueData["fixed_version_id"])
|
||||
copyNestedIDValue(metadata, "fixed_version_id", issueData["fixed_version"])
|
||||
copyIDValue(metadata, "assigned_to_id", issueData["assigned_to_id"])
|
||||
copyNestedIDValue(metadata, "assigned_to_id", issueData["assigned_to"])
|
||||
|
||||
if ids := issueTagIDs(issueData["issue_tags"]); len(ids) > 0 {
|
||||
metadata["issue_tag_ids"] = ids
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
func copyIssueMetadata(body map[string]interface{}, metadata map[string]interface{}) {
|
||||
for key, value := range metadata {
|
||||
body[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
func copyNestedIDValue(dst map[string]interface{}, dstKey string, value interface{}) {
|
||||
object, ok := value.(map[string]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
copyIDValue(dst, dstKey, object["id"])
|
||||
}
|
||||
|
||||
func copyIDValue(dst map[string]interface{}, dstKey string, value interface{}) {
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
dst[dstKey] = v
|
||||
case int64:
|
||||
dst[dstKey] = v
|
||||
case float64:
|
||||
dst[dstKey] = int(v)
|
||||
case string:
|
||||
if strings.TrimSpace(v) != "" {
|
||||
dst[dstKey] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func issueTagIDs(value interface{}) []interface{} {
|
||||
tags, ok := value.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
ids := make([]interface{}, 0, len(tags))
|
||||
for _, tag := range tags {
|
||||
tagData, ok := tag.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if id, ok := normalizedIDValue(tagData["id"]); ok {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func normalizedIDValue(value interface{}) (interface{}, bool) {
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
return v, true
|
||||
case int64:
|
||||
return v, true
|
||||
case float64:
|
||||
return int(v), true
|
||||
case string:
|
||||
if strings.TrimSpace(v) != "" {
|
||||
return v, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func normalizeIssueStatus(state string) (interface{}, error) {
|
||||
switch strings.ToLower(strings.TrimSpace(state)) {
|
||||
case "open":
|
||||
|
|
|
|||
|
|
@ -100,6 +100,55 @@ 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",
|
||||
"priority": map[string]interface{}{"id": 2, "name": "normal"},
|
||||
"tracker": map[string]interface{}{"id": 1, "name": "bug"},
|
||||
"fixed_version": map[string]interface{}{"id": 9, "name": "v1"},
|
||||
"assigned_to_id": 7,
|
||||
"issue_tags": []map[string]interface{}{
|
||||
{"id": 3, "name": "bug"},
|
||||
{"id": 4, "name": "cli"},
|
||||
},
|
||||
})
|
||||
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["priority_id"], float64(2))
|
||||
assertEqual(t, updatePayload["tracker_id"], float64(1))
|
||||
assertEqual(t, updatePayload["fixed_version_id"], float64(9))
|
||||
assertEqual(t, updatePayload["assigned_to_id"], float64(7))
|
||||
|
||||
tagIDs, ok := updatePayload["issue_tag_ids"].([]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("issue_tag_ids = %T, want []interface{}", updatePayload["issue_tag_ids"])
|
||||
}
|
||||
assertEqual(t, tagIDs[0], float64(3))
|
||||
assertEqual(t, tagIDs[1], float64(4))
|
||||
}
|
||||
|
||||
func TestBatchClosePreservesCurrentDescription(t *testing.T) {
|
||||
var updatePayload map[string]interface{}
|
||||
server := newIssueTestServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue