From 1783a0b217da870e162fac87b4293a985151ed61 Mon Sep 17 00:00:00 2001 From: Damian ONeill Date: Wed, 23 Aug 2023 09:10:44 +0100 Subject: [PATCH] feat: affects version (#642) --- internal/cmd/epic/create/create.go | 55 ++++++++-------- internal/cmd/issue/create/create.go | 59 +++++++++-------- internal/cmd/issue/edit/edit.go | 99 +++++++++++++++++++---------- internal/cmdcommon/create.go | 54 ++++++++++------ pkg/jira/create.go | 35 +++++++--- pkg/jira/create_test.go | 19 +++--- pkg/jira/edit.go | 65 ++++++++++++++++--- pkg/jira/types.go | 3 + 8 files changed, 256 insertions(+), 133 deletions(-) diff --git a/internal/cmd/epic/create/create.go b/internal/cmd/epic/create/create.go index 24778895..11197329 100644 --- a/internal/cmd/epic/create/create.go +++ b/internal/cmd/epic/create/create.go @@ -98,18 +98,19 @@ func create(cmd *cobra.Command, _ []string) { defer s.Stop() cr := jira.CreateRequest{ - Project: project, - IssueType: jira.IssueTypeEpic, - Summary: params.Summary, - Body: params.Body, - Reporter: params.Reporter, - Assignee: params.Assignee, - Priority: params.Priority, - Labels: params.Labels, - Components: params.Components, - FixVersions: params.FixVersions, - CustomFields: params.CustomFields, - EpicField: viper.GetString("epic.name"), + Project: project, + IssueType: jira.IssueTypeEpic, + Summary: params.Summary, + Body: params.Body, + Reporter: params.Reporter, + Assignee: params.Assignee, + Priority: params.Priority, + Labels: params.Labels, + Components: params.Components, + FixVersions: params.FixVersions, + AffectsVersions: params.AffectsVersions, + CustomFields: params.CustomFields, + EpicField: viper.GetString("epic.name"), } if projectType != jira.ProjectTypeNextGen { cr.Name = params.Name @@ -229,6 +230,9 @@ func parseFlags(flags query.FlagParser) *cmdcommon.CreateParams { fixVersions, err := flags.GetStringArray("fix-version") cmdutil.ExitIfError(err) + affectsVersions, err := flags.GetStringArray("affects-version") + cmdutil.ExitIfError(err) + custom, err := flags.GetStringToString("custom") cmdutil.ExitIfError(err) @@ -242,18 +246,19 @@ func parseFlags(flags query.FlagParser) *cmdcommon.CreateParams { cmdutil.ExitIfError(err) return &cmdcommon.CreateParams{ - Name: name, - Summary: summary, - Body: body, - Priority: priority, - Reporter: reporter, - Assignee: assignee, - Labels: labels, - Components: components, - FixVersions: fixVersions, - CustomFields: custom, - Template: template, - NoInput: noInput, - Debug: debug, + Name: name, + Summary: summary, + Body: body, + Priority: priority, + Reporter: reporter, + Assignee: assignee, + Labels: labels, + Components: components, + FixVersions: fixVersions, + AffectsVersions: affectsVersions, + CustomFields: custom, + Template: template, + NoInput: noInput, + Debug: debug, } } diff --git a/internal/cmd/issue/create/create.go b/internal/cmd/issue/create/create.go index 52129980..d2122682 100644 --- a/internal/cmd/issue/create/create.go +++ b/internal/cmd/issue/create/create.go @@ -99,19 +99,20 @@ func create(cmd *cobra.Command, _ []string) { defer s.Stop() cr := jira.CreateRequest{ - Project: project, - IssueType: params.IssueType, - ParentIssueKey: params.ParentIssueKey, - Summary: params.Summary, - Body: params.Body, - Reporter: params.Reporter, - Assignee: params.Assignee, - Priority: params.Priority, - Labels: params.Labels, - Components: params.Components, - FixVersions: params.FixVersions, - CustomFields: params.CustomFields, - EpicField: viper.GetString("epic.link"), + Project: project, + IssueType: params.IssueType, + ParentIssueKey: params.ParentIssueKey, + Summary: params.Summary, + Body: params.Body, + Reporter: params.Reporter, + Assignee: params.Assignee, + Priority: params.Priority, + Labels: params.Labels, + Components: params.Components, + FixVersions: params.FixVersions, + AffectsVersions: params.AffectsVersions, + CustomFields: params.CustomFields, + EpicField: viper.GetString("epic.link"), } cr.ForProjectType(projectType) cr.ForInstallationType(installation) @@ -343,6 +344,9 @@ func parseFlags(flags query.FlagParser) *cmdcommon.CreateParams { fixVersions, err := flags.GetStringArray("fix-version") cmdutil.ExitIfError(err) + affectsVersions, err := flags.GetStringArray("affects-version") + cmdutil.ExitIfError(err) + custom, err := flags.GetStringToString("custom") cmdutil.ExitIfError(err) @@ -356,19 +360,20 @@ func parseFlags(flags query.FlagParser) *cmdcommon.CreateParams { cmdutil.ExitIfError(err) return &cmdcommon.CreateParams{ - IssueType: issueType, - ParentIssueKey: parentIssueKey, - Summary: summary, - Body: body, - Priority: priority, - Assignee: assignee, - Labels: labels, - Reporter: reporter, - Components: components, - FixVersions: fixVersions, - CustomFields: custom, - Template: template, - NoInput: noInput, - Debug: debug, + IssueType: issueType, + ParentIssueKey: parentIssueKey, + Summary: summary, + Body: body, + Priority: priority, + Assignee: assignee, + Labels: labels, + Reporter: reporter, + Components: components, + FixVersions: fixVersions, + AffectsVersions: affectsVersions, + CustomFields: custom, + Template: template, + NoInput: noInput, + Debug: debug, } } diff --git a/internal/cmd/issue/edit/edit.go b/internal/cmd/issue/edit/edit.go index c48e43ce..c4977181 100644 --- a/internal/cmd/issue/edit/edit.go +++ b/internal/cmd/issue/edit/edit.go @@ -127,6 +127,12 @@ func edit(cmd *cobra.Command, args []string) { } fixVersions = append(fixVersions, params.fixVersions...) + affectsVersions := make([]string, 0, len(issue.Fields.AffectsVersions)+len(params.affectsVersions)) + for _, fv := range issue.Fields.AffectsVersions { + affectsVersions = append(affectsVersions, fv.Name) + } + affectsVersions = append(affectsVersions, params.affectsVersions...) + err = func() error { s := cmdutil.Info("Updating an issue...") defer s.Stop() @@ -142,14 +148,15 @@ func edit(cmd *cobra.Command, args []string) { } edr := jira.EditRequest{ - ParentIssueKey: parent, - Summary: params.summary, - Body: body, - Priority: params.priority, - Labels: labels, - Components: components, - FixVersions: fixVersions, - CustomFields: params.customFields, + ParentIssueKey: parent, + Summary: params.summary, + Body: body, + Priority: params.priority, + Labels: labels, + Components: components, + FixVersions: fixVersions, + AffectsVersions: affectsVersions, + CustomFields: params.customFields, } if configuredCustomFields, err := cmdcommon.GetConfiguredCustomFields(); err == nil { cmdcommon.ValidateCustomFields(edr.CustomFields, configuredCustomFields) @@ -187,10 +194,11 @@ func getAnswers(params *editParams, issue *jira.Issue) { if len(ans.Metadata) > 0 { qs := getMetadataQuestions(ans.Metadata, issue) ans := struct { - Priority string - Labels string - Components string - FixVersions string + Priority string + Labels string + Components string + FixVersions string + AffectsVersions string }{} err := survey.Ask(qs, &ans) cmdutil.ExitIfError(err) @@ -207,6 +215,9 @@ func getAnswers(params *editParams, issue *jira.Issue) { if len(ans.FixVersions) > 0 { params.fixVersions = strings.Split(ans.FixVersions, ",") } + if len(ans.AffectsVersions) > 0 { + params.affectsVersions = strings.Split(ans.AffectsVersions, ",") + } } } } @@ -289,17 +300,18 @@ 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 - customFields map[string]string - noInput bool - debug bool + issueKey string + summary string + body string + priority string + assignee string + labels []string + components []string + fixVersions []string + affectsVersions []string + customFields map[string]string + noInput bool + debug bool } func parseArgsAndFlags(flags query.FlagParser, args []string, project string) *editParams { @@ -324,6 +336,9 @@ func parseArgsAndFlags(flags query.FlagParser, args []string, project string) *e fixVersions, err := flags.GetStringArray("fix-version") cmdutil.ExitIfError(err) + affectsVersions, err := flags.GetStringArray("affects-version") + cmdutil.ExitIfError(err) + custom, err := flags.GetStringToString("custom") cmdutil.ExitIfError(err) @@ -334,17 +349,18 @@ func parseArgsAndFlags(flags query.FlagParser, args []string, project string) *e 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, - customFields: custom, - noInput: noInput, - debug: debug, + issueKey: cmdutil.GetJiraIssueKey(project, args[0]), + summary: summary, + body: body, + priority: priority, + assignee: assignee, + labels: labels, + components: components, + fixVersions: fixVersions, + affectsVersions: affectsVersions, + customFields: custom, + noInput: noInput, + debug: debug, } } @@ -356,6 +372,11 @@ func getMetadataQuestions(meta []string, issue *jira.Issue) []*survey.Question { fixVersions = append(fixVersions, fv.Name) } + affectsVersions := make([]string, 0, len(issue.Fields.AffectsVersions)) + for _, fv := range issue.Fields.AffectsVersions { + affectsVersions = append(affectsVersions, fv.Name) + } + for _, m := range meta { switch m { case "Priority": @@ -389,6 +410,15 @@ func getMetadataQuestions(meta []string, issue *jira.Issue) []*survey.Question { Default: strings.Join(fixVersions, ","), }, }) + case "AffectsVersions": + qs = append(qs, &survey.Question{ + Name: "affectsversions", + Prompt: &survey.Input{ + Message: "Affects Versions", + Help: "Comma separated list of affectsVersions. For eg: v1.0-beta,v2.0", + Default: strings.Join(affectsVersions, ","), + }, + }) } } @@ -407,6 +437,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().StringArray("affects-version", []string{}, "Add/Append release info (affectsVersions)") 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") diff --git a/internal/cmdcommon/create.go b/internal/cmdcommon/create.go index d0869763..47ef9933 100644 --- a/internal/cmdcommon/create.go +++ b/internal/cmdcommon/create.go @@ -23,21 +23,22 @@ const ( // CreateParams holds parameters for create command. type CreateParams struct { - Name string - IssueType string - ParentIssueKey string - Summary string - Body string - Priority string - Reporter string - Assignee string - Labels []string - Components []string - FixVersions []string - CustomFields map[string]string - Template string - NoInput bool - Debug bool + Name string + IssueType string + ParentIssueKey string + Summary string + Body string + Priority string + Reporter string + Assignee string + Labels []string + Components []string + FixVersions []string + AffectsVersions []string + CustomFields map[string]string + Template string + NoInput bool + Debug bool } // SetCreateFlags sets flags supported by create command. @@ -61,6 +62,7 @@ And, this field is mandatory when creating a sub-task.`) cmd.Flags().StringArrayP("label", "l", []string{}, prefix+" labels") cmd.Flags().StringArrayP("component", "C", []string{}, prefix+" components") cmd.Flags().StringArray("fix-version", []string{}, "Release info (fixVersions)") + cmd.Flags().StringArray("affects-version", []string{}, "Release info (affectsVersions)") cmd.Flags().StringToString("custom", custom, "Set custom fields") cmd.Flags().StringP("template", "T", "", "Path to a file to read body/description from") cmd.Flags().Bool("web", false, "Open in web browser after successful creation") @@ -90,7 +92,7 @@ func GetMetadata() []*survey.Question { Name: "metadata", Prompt: &survey.MultiSelect{ Message: "What would you like to add?", - Options: []string{"Priority", "Components", "Labels", "FixVersions"}, + Options: []string{"Priority", "Components", "Labels", "FixVersions", "AffectsVersions"}, }, }, } @@ -131,6 +133,14 @@ func GetMetadataQuestions(cat []string) []*survey.Question { Help: "Comma separated list of fixVersions. For eg: v1.0-beta,v2.0", }, }) + case "AffectsVersions": + qs = append(qs, &survey.Question{ + Name: "affectsversions", + Prompt: &survey.Input{ + Message: "Affects Versions", + Help: "Comma separated list of affectsVersions. For eg: v1.0-beta,v2.0", + }, + }) } } @@ -159,10 +169,11 @@ func HandleNoInput(params *CreateParams) error { if len(ans.Metadata) > 0 { qs := GetMetadataQuestions(ans.Metadata) ans := struct { - Priority string - Labels string - Components string - FixVersions string + Priority string + Labels string + Components string + FixVersions string + AffectsVersions string }{} err := survey.Ask(qs, &ans) if err != nil { @@ -181,6 +192,9 @@ func HandleNoInput(params *CreateParams) error { if len(ans.FixVersions) > 0 { params.FixVersions = strings.Split(ans.FixVersions, ",") } + if len(ans.AffectsVersions) > 0 { + params.AffectsVersions = strings.Split(ans.AffectsVersions, ",") + } } } } diff --git a/pkg/jira/create.go b/pkg/jira/create.go index 6785a977..0f4edbd7 100644 --- a/pkg/jira/create.go +++ b/pkg/jira/create.go @@ -24,15 +24,16 @@ type CreateRequest struct { IssueType string // ParentIssueKey is required when creating a sub-task for classic project. // This can also be used to attach epic for next-gen project. - ParentIssueKey string - Summary string - Body interface{} // string in v1/v2 and adf.ADF in v3 - Reporter string - Assignee string - Priority string - Labels []string - Components []string - FixVersions []string + ParentIssueKey string + Summary string + Body interface{} // string in v1/v2 and adf.ADF in v3 + Reporter string + Assignee string + Priority string + Labels []string + Components []string + FixVersions []string + AffectsVersions []string // EpicField is the dynamic epic field name // that changes per jira installation. EpicField string @@ -202,6 +203,18 @@ func (*Client) getRequestData(req *CreateRequest) *createRequest { } data.Fields.M.FixVersions = versions } + if len(req.AffectsVersions) > 0 { + versions := make([]struct { + Name string `json:"name,omitempty"` + }, 0, len(req.AffectsVersions)) + + for _, v := range req.AffectsVersions { + versions = append(versions, struct { + Name string `json:"name,omitempty"` + }{v}) + } + data.Fields.M.AffectsVersions = versions + } constructCustomFields(req.CustomFields, req.configuredCustomFields, &data) return &data @@ -287,7 +300,9 @@ type createFields struct { FixVersions []struct { Name string `json:"name,omitempty"` } `json:"fixVersions,omitempty"` - + AffectsVersions []struct { + Name string `json:"name,omitempty"` + } `json:"versions,omitempty"` epicField string customFields customField } diff --git a/pkg/jira/create_test.go b/pkg/jira/create_test.go index 1409d1a5..e1320d76 100644 --- a/pkg/jira/create_test.go +++ b/pkg/jira/create_test.go @@ -46,7 +46,7 @@ func (c *createTestServer) statusCode(code int) { func TestCreate(t *testing.T) { expectedBody := `{"update":{},"fields":{"project":{"key":"TEST"},"issuetype":{"name":"Bug"},` + `"summary":"Test bug","description":"Test description","priority":{"name":"Normal"},"labels":["test","dev"],` + - `"components":[{"name":"BE"},{"name":"FE"}],"fixVersions":[{"name":"v2.0"},{"name":"v2.1-hotfix"}]}}` + `"components":[{"name":"BE"},{"name":"FE"}],"fixVersions":[{"name":"v2.0"},{"name":"v2.1-hotfix"}],"versions":[{"name":"v3.0"},{"name":"v3.1-hotfix"}]}}` testServer := createTestServer{code: 201} server := testServer.serve(t, expectedBody) defer server.Close() @@ -54,14 +54,15 @@ func TestCreate(t *testing.T) { client := NewClient(Config{Server: server.URL}, WithTimeout(3*time.Second)) requestData := CreateRequest{ - Project: "TEST", - IssueType: "Bug", - Summary: "Test bug", - Body: "Test description", - Priority: "Normal", - Labels: []string{"test", "dev"}, - Components: []string{"BE", "FE"}, - FixVersions: []string{"v2.0", "v2.1-hotfix"}, + Project: "TEST", + IssueType: "Bug", + Summary: "Test bug", + Body: "Test description", + Priority: "Normal", + Labels: []string{"test", "dev"}, + Components: []string{"BE", "FE"}, + FixVersions: []string{"v2.0", "v2.1-hotfix"}, + AffectsVersions: []string{"v3.0", "v3.1-hotfix"}, } actual, err := client.CreateV2(&requestData) assert.NoError(t, err) diff --git a/pkg/jira/edit.go b/pkg/jira/edit.go index 323fd2b4..6c96c336 100644 --- a/pkg/jira/edit.go +++ b/pkg/jira/edit.go @@ -19,14 +19,15 @@ type EditResponse struct { // EditRequest struct holds request data for edit request. // Setting an Assignee requires an account ID. type EditRequest struct { - IssueType string - ParentIssueKey string - Summary string - Body string - Priority string - Labels []string - Components []string - FixVersions []string + IssueType string + ParentIssueKey string + Summary string + Body string + Priority string + Labels []string + Components []string + FixVersions []string + AffectsVersions []string // CustomFields holds all custom fields passed // while editing the issue. CustomFields map[string]string @@ -99,6 +100,14 @@ type editFields struct { Name string `json:"name,omitempty"` } `json:"remove,omitempty"` } `json:"fixVersions,omitempty"` + AffectsVersions []struct { + Add *struct { + Name string `json:"name,omitempty"` + } `json:"add,omitempty"` + Remove *struct { + Name string `json:"name,omitempty"` + } `json:"remove,omitempty"` + } `json:"versions,omitempty"` customFields customField } @@ -276,6 +285,46 @@ func getRequestDataForEdit(req *EditRequest) *editRequest { update.M.FixVersions = versions } + if len(req.AffectsVersions) > 0 { + add, sub := splitAddAndRemove(req.AffectsVersions) + + versions := make([]struct { + Add *struct { + Name string `json:"name,omitempty"` + } `json:"add,omitempty"` + Remove *struct { + Name string `json:"name,omitempty"` + } `json:"remove,omitempty"` + }, 0, len(req.AffectsVersions)) + + for _, v := range sub { + versions = append(versions, struct { + Add *struct { + Name string `json:"name,omitempty"` + } `json:"add,omitempty"` + Remove *struct { + Name string `json:"name,omitempty"` + } `json:"remove,omitempty"` + }{Remove: &struct { + Name string `json:"name,omitempty"` + }{Name: v}}) + } + for _, v := range add { + versions = append(versions, struct { + Add *struct { + Name string `json:"name,omitempty"` + } `json:"add,omitempty"` + Remove *struct { + Name string `json:"name,omitempty"` + } `json:"remove,omitempty"` + }{Add: &struct { + Name string `json:"name,omitempty"` + }{Name: v}}) + } + + update.M.AffectsVersions = versions + } + fields := struct { Parent *struct { Key string `json:"key,omitempty"` diff --git a/pkg/jira/types.go b/pkg/jira/types.go index 8dea2427..ac785127 100644 --- a/pkg/jira/types.go +++ b/pkg/jira/types.go @@ -87,6 +87,9 @@ type IssueFields struct { FixVersions []struct { Name string `json:"name"` } `json:"fixVersions"` + AffectsVersions []struct { + Name string `json:"name"` + } `json:"versions"` Comment struct { Comments []struct { ID string `json:"id"`