Skip to content

Commit

Permalink
feat: Support custom fields on issue edit (#377)
Browse files Browse the repository at this point in the history
  • Loading branch information
ankitpokhrel authored May 29, 2022
1 parent ae23022 commit 85d8a7e
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 21 deletions.
49 changes: 29 additions & 20 deletions internal/cmd/issue/edit/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ func edit(cmd *cobra.Command, args []string) {
Labels: labels,
Components: components,
FixVersions: fixVersions,
CustomFields: params.customFields,
}

return client.Edit(params.issueKey, &edr)
Expand Down Expand Up @@ -278,16 +279,17 @@ func (ec *editCmd) askQuestions(issue *jira.Issue, originalBody string) error {
}

type editParams struct {
issueKey string
summary string
body string
priority string
assignee string
labels []string
components []string
fixVersions []string
noInput bool
debug bool
issueKey string
summary string
body string
priority string
assignee string
labels []string
components []string
fixVersions []string
customFields map[string]string
noInput bool
debug bool
}

func (ep *editParams) isEmpty() bool {
Expand Down Expand Up @@ -317,23 +319,27 @@ func parseArgsAndFlags(flags query.FlagParser, args []string, project string) *e
fixVersions, err := flags.GetStringArray("fix-version")
cmdutil.ExitIfError(err)

custom, err := flags.GetStringToString("custom")
cmdutil.ExitIfError(err)

noInput, err := flags.GetBool("no-input")
cmdutil.ExitIfError(err)

debug, err := flags.GetBool("debug")
cmdutil.ExitIfError(err)

return &editParams{
issueKey: cmdutil.GetJiraIssueKey(project, args[0]),
summary: summary,
body: body,
priority: priority,
assignee: assignee,
labels: labels,
components: components,
fixVersions: fixVersions,
noInput: noInput,
debug: debug,
issueKey: cmdutil.GetJiraIssueKey(project, args[0]),
summary: summary,
body: body,
priority: priority,
assignee: assignee,
labels: labels,
components: components,
fixVersions: fixVersions,
customFields: custom,
noInput: noInput,
debug: debug,
}
}

Expand Down Expand Up @@ -385,6 +391,8 @@ func getMetadataQuestions(meta []string, issue *jira.Issue) []*survey.Question {
}

func setFlags(cmd *cobra.Command) {
custom := make(map[string]string)

cmd.Flags().SortFlags = false

cmd.Flags().StringP("summary", "s", "", "Edit summary or title")
Expand All @@ -394,6 +402,7 @@ func setFlags(cmd *cobra.Command) {
cmd.Flags().StringArrayP("label", "l", []string{}, "Append labels")
cmd.Flags().StringArrayP("component", "C", []string{}, "Replace components")
cmd.Flags().StringArray("fix-version", []string{}, "Add/Append release info (fixVersions)")
cmd.Flags().StringToString("custom", custom, "Edit custom fields")
cmd.Flags().Bool("web", false, "Open in web browser after successful update")
cmd.Flags().Bool("no-input", false, "Disable prompt for non-required fields")
}
17 changes: 17 additions & 0 deletions pkg/jira/customfield.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@ type customField map[string]interface{}

type customFieldTypeNumber float64

type customFieldTypeNumberSet struct {
Set customFieldTypeNumber `json:"set"`
}

type customFieldTypeStringSet struct {
Set string `json:"set"`
}

type customFieldTypeOption struct {
Value string `json:"value"`
}

type customFieldTypeOptionSet struct {
Set customFieldTypeOption `json:"set"`
}

type customFieldTypeOptionAddRemove struct {
Add *customFieldTypeOption `json:"add,omitempty"`
Remove *customFieldTypeOption `json:"remove,omitempty"`
}
80 changes: 79 additions & 1 deletion pkg/jira/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import (
"context"
"encoding/json"
"net/http"
"strconv"
"strings"

"github.com/spf13/viper"
)

const separatorMinus = "-"
Expand All @@ -26,6 +29,9 @@ type EditRequest struct {
Labels []string
Components []string
FixVersions []string
// CustomFields holds all custom fields passed
// while editing the issue.
CustomFields map[string]string
}

// Edit updates an issue using POST /issue endpoint.
Expand Down Expand Up @@ -88,6 +94,8 @@ type editFields struct {
Name string `json:"name,omitempty"`
} `json:"remove,omitempty"`
} `json:"fixVersions,omitempty"`

customFields customField
}

type editFieldsMarshaler struct {
Expand All @@ -112,7 +120,22 @@ func (cfm *editFieldsMarshaler) MarshalJSON() ([]byte, error) {
cfm.M.Labels = nil
}

return json.Marshal(cfm.M)
m, err := json.Marshal(cfm.M)
if err != nil {
return m, err
}

var temp interface{}
if err := json.Unmarshal(m, &temp); err != nil {
return nil, err
}
dm := temp.(map[string]interface{})

for key, val := range cfm.M.customFields {
dm[key] = val
}

return json.Marshal(dm)
}

type editRequest struct {
Expand Down Expand Up @@ -271,10 +294,65 @@ func getRequestDataForEdit(req *EditRequest) *editRequest {
Update: update,
Fields: fields,
}
constructCustomFieldsForEdit(req.CustomFields, &data)

return &data
}

func constructCustomFieldsForEdit(fields map[string]string, data *editRequest) {
if len(fields) == 0 {
return
}

var configuredFields []IssueTypeField

err := viper.UnmarshalKey("issue.fields.custom", &configuredFields)
if err != nil || len(configuredFields) == 0 {
return
}

data.Update.M.customFields = make(customField)

for key, val := range fields {
for _, configured := range configuredFields {
identifier := strings.ReplaceAll(strings.ToLower(strings.TrimSpace(configured.Name)), " ", "-")
if identifier != strings.ToLower(key) {
continue
}

switch configured.Schema.DataType {
case customFieldFormatOption:
data.Update.M.customFields[configured.Key] = []customFieldTypeOptionSet{{Set: customFieldTypeOption{Value: val}}}
case customFieldFormatArray:
pieces := strings.Split(strings.TrimSpace(val), ",")
if configured.Schema.Items == customFieldFormatOption {
items := make([]customFieldTypeOptionAddRemove, 0)
for _, p := range pieces {
if strings.HasPrefix(p, separatorMinus) {
items = append(items, customFieldTypeOptionAddRemove{Remove: &customFieldTypeOption{Value: strings.TrimPrefix(p, separatorMinus)}})
} else {
items = append(items, customFieldTypeOptionAddRemove{Add: &customFieldTypeOption{Value: p}})
}
}
data.Update.M.customFields[configured.Key] = items
} else {
data.Update.M.customFields[configured.Key] = pieces
}
case customFieldFormatNumber:
num, err := strconv.ParseFloat(val, 64) //nolint:gomnd
if err != nil {
// Let Jira API handle data type error for now.
data.Update.M.customFields[configured.Key] = []customFieldTypeStringSet{{Set: val}}
} else {
data.Update.M.customFields[configured.Key] = []customFieldTypeNumberSet{{Set: customFieldTypeNumber(num)}}
}
default:
data.Update.M.customFields[configured.Key] = []customFieldTypeStringSet{{Set: val}}
}
}
}
}

func splitAddAndRemove(input []string) ([]string, []string) {
add := make([]string, 0, len(input))
sub := make([]string, 0, len(input))
Expand Down

0 comments on commit 85d8a7e

Please sign in to comment.