From 1b66405f39c342742985e413376cc654becf34c6 Mon Sep 17 00:00:00 2001 From: Miguel de la Cruz Date: Thu, 5 Mar 2020 20:42:25 +0100 Subject: [PATCH] Allow standalone command to create issues --- cmd/add.go | 8 ++-- cmd/init.go | 20 +++++---- cmd/publish.go | 22 ++++++++-- cmd/standalone.go | 73 +++++++++++++------------------ jira/jira.go | 106 +++++++++++++++++++++++++++++++++------------- model/campaign.go | 12 +++--- 6 files changed, 148 insertions(+), 93 deletions(-) diff --git a/cmd/add.go b/cmd/add.go index d5de862..f5223e4 100644 --- a/cmd/add.go +++ b/cmd/add.go @@ -23,7 +23,7 @@ func GrepAddCmd() *cobra.Command { Run: grepAddCmdF, } - cmd.Flags().BoolP("file-only", "f", false, "generates one ticket per file instead of per match") + cmd.Flags().BoolP("file-only", "f", false, "Generates one ticket per file instead of per match") return cmd } @@ -38,7 +38,7 @@ func AgAddCmd() *cobra.Command { RunE: agAddCmdF, } - cmd.Flags().BoolP("file-only", "f", false, "generates one ticket per file instead of per match") + cmd.Flags().BoolP("file-only", "f", false, "Generates one ticket per file instead of per match") return cmd } @@ -53,7 +53,7 @@ func GovetAddCmd() *cobra.Command { RunE: govetAddCmdF, } - cmd.Flags().BoolP("file-only", "f", false, "generates one ticket per file instead of per match") + cmd.Flags().BoolP("file-only", "f", false, "Generates one ticket per file instead of per match") return cmd } @@ -67,7 +67,7 @@ func CsvAddCmd() *cobra.Command { RunE: csvAddCmdF, } - cmd.Flags().BoolP("file-only", "f", false, "generates one ticket per file instead of per match") + cmd.Flags().BoolP("file-only", "f", false, "Generates one ticket per file instead of per match") return cmd } diff --git a/cmd/init.go b/cmd/init.go index 6b9648d..71a1b71 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -15,14 +15,16 @@ func InitCmd() *cobra.Command { Run: initCmdF, } - cmd.Flags().StringP("url", "u", "", "the jira server URL") + cmd.Flags().StringP("url", "u", "", "The jira server URL") _ = cmd.MarkFlagRequired("url") - cmd.Flags().StringP("project", "p", "", "the jira project key to associate the tickets with") + cmd.Flags().StringP("project", "p", "", "The jira project key to associate the tickets with") _ = cmd.MarkFlagRequired("project") - cmd.Flags().StringP("epic", "e", "", "the epic id to associate this campaign with") + cmd.Flags().StringP("epic", "e", "", "The epic id to associate this campaign with") _ = cmd.MarkFlagRequired("epic") - cmd.Flags().StringP("summary", "s", "", "the summary of the tickets") + cmd.Flags().StringP("summary", "s", "", "The summary of the tickets") _ = cmd.MarkFlagRequired("summary") + cmd.Flags().StringP("template", "t", "", "The template path for the description of the tickets") + _ = cmd.MarkFlagRequired("template") return cmd } @@ -32,12 +34,14 @@ func initCmdF(cmd *cobra.Command, _ []string) { project, _ := cmd.Flags().GetString("project") epic, _ := cmd.Flags().GetString("epic") summary, _ := cmd.Flags().GetString("summary") + template, _ := cmd.Flags().GetString("template") cmp := &model.Campaign{ - Url: url, - Project: project, - Epic: epic, - Summary: summary, + Url: url, + Project: project, + Epic: epic, + Summary: summary, + Template: template, } if err := campaign.Save(cmp); err != nil { ErrorAndExit(cmd, err) diff --git a/cmd/publish.go b/cmd/publish.go index d37ef4b..2000cab 100644 --- a/cmd/publish.go +++ b/cmd/publish.go @@ -5,12 +5,17 @@ import ( ) func JiraPublishCmd() *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "jira", Short: "Publishes the campaign tickets in JIRA", Args: cobra.NoArgs, - Run: jiraPublishCmdF, + RunE: jiraPublishCmdF, } + + cmd.Flags().BoolP("all", "a", false, "Publish all the tickets of the campaign") + cmd.Flags().IntP("batch", "b", 0, "Number of tickets to publish") + + return cmd } func PublishCmd() *cobra.Command { @@ -26,4 +31,15 @@ func PublishCmd() *cobra.Command { return cmd } -func jiraPublishCmdF(_ *cobra.Command, _ []string) {} +func jiraPublishCmdF(cmd *cobra.Command, _ []string) error { + /* + all, _ := cmd.Flags().GetBool("all") + batch, _ := cmd.Flags().GetInt("batch") + + if !all && batch == 0 { + return fmt.Errorf("One of --all or --batch flags is required") + } + */ + + return nil +} diff --git a/cmd/standalone.go b/cmd/standalone.go index e8ee500..d392047 100644 --- a/cmd/standalone.go +++ b/cmd/standalone.go @@ -1,13 +1,12 @@ package cmd import ( - "bytes" "fmt" "strings" - "text/template" "git.ctrlz.es/mgdelacroix/campaigner/config" "git.ctrlz.es/mgdelacroix/campaigner/jira" + "git.ctrlz.es/mgdelacroix/campaigner/model" "github.com/spf13/cobra" ) @@ -34,19 +33,19 @@ func CreateJiraTicketStandaloneCmd() *cobra.Command { RunE: createJiraTicketStandaloneCmdF, } - cmd.Flags().String("url", "", "the jira server URL") + cmd.Flags().String("url", "", "The jira server URL") _ = cmd.MarkFlagRequired("url") - cmd.Flags().String("epic", "", "the jira epic id to associate the ticket with") + cmd.Flags().String("epic", "", "The jira epic id to associate the ticket with") _ = cmd.MarkFlagRequired("epic") - cmd.Flags().String("team", "", "the team for the new ticket") - _ = cmd.MarkFlagRequired("epic") - cmd.Flags().String("username", "", "the jira username") - cmd.Flags().String("token", "", "the jira token") - cmd.Flags().String("summary", "", "the summary of the ticket") + cmd.Flags().StringP("project", "p", "", "The jira project key to associate the tickets with") + _ = cmd.MarkFlagRequired("project") + cmd.Flags().String("summary", "", "The summary of the ticket") _ = cmd.MarkFlagRequired("summary") - cmd.Flags().String("template", "", "the template to render the description of the ticket") + cmd.Flags().String("template", "", "The template to render the description of the ticket") _ = cmd.MarkFlagRequired("template") - cmd.Flags().StringSliceP("vars", "v", []string{}, "the variables to use in the template") + cmd.Flags().String("username", "", "The jira username") + cmd.Flags().String("token", "", "The jira token") + cmd.Flags().StringSliceP("vars", "v", []string{}, "The variables to use in the template") return cmd } @@ -59,16 +58,16 @@ func GetJiraTicketStandaloneCmd() *cobra.Command { Run: getJiraTicketStandaloneCmdF, } - cmd.Flags().String("url", "", "the jira server URL") + cmd.Flags().String("url", "", "The jira server URL") _ = cmd.MarkFlagRequired("url") - cmd.Flags().String("username", "", "the jira username") - cmd.Flags().String("token", "", "the jira token") + cmd.Flags().String("username", "", "The jira username") + cmd.Flags().String("token", "", "The jira token") return cmd } -func getVarMap(vars []string) (map[string]string, error) { - varMap := map[string]string{} +func getVarMap(vars []string) (map[string]interface{}, error) { + varMap := map[string]interface{}{} for _, v := range vars { parts := strings.Split(v, "=") if len(parts) < 2 { @@ -81,12 +80,12 @@ func getVarMap(vars []string) (map[string]string, error) { func createJiraTicketStandaloneCmdF(cmd *cobra.Command, _ []string) error { url, _ := cmd.Flags().GetString("url") - epicId, _ := cmd.Flags().GetString("epic") - team, _ := cmd.Flags().GetString("team") + epic, _ := cmd.Flags().GetString("epic") + project, _ := cmd.Flags().GetString("project") username, _ := cmd.Flags().GetString("username") token, _ := cmd.Flags().GetString("token") - summaryTmplStr, _ := cmd.Flags().GetString("summary") - templatePath, _ := cmd.Flags().GetString("template") + summary, _ := cmd.Flags().GetString("summary") + template, _ := cmd.Flags().GetString("template") vars, _ := cmd.Flags().GetStringSlice("vars") if username == "" || token == "" { @@ -108,39 +107,25 @@ func createJiraTicketStandaloneCmdF(cmd *cobra.Command, _ []string) error { return fmt.Errorf("error processing vars: %w", err) } - sumTmpl, err := template.New("").Parse(summaryTmplStr) - if err != nil { - ErrorAndExit(cmd, err) - } - - var summaryBytes bytes.Buffer - if err := sumTmpl.Execute(&summaryBytes, varMap); err != nil { - ErrorAndExit(cmd, err) - } - summary := summaryBytes.String() - - descTmpl, err := template.ParseFiles(templatePath) - if err != nil { - ErrorAndExit(cmd, err) - } - - var descriptionBytes bytes.Buffer - if err := descTmpl.Execute(&descriptionBytes, varMap); err != nil { - ErrorAndExit(cmd, err) - } - description := descriptionBytes.String() - jiraClient, err := jira.NewClient(url, username, token) if err != nil { ErrorAndExit(cmd, err) } - ticketKey, err := jiraClient.CreateIssue(epicId, team, summary, description) + campaign := &model.Campaign{ + Epic: epic, + Project: project, + Summary: summary, + Template: template, + } + ticket := &model.Ticket{Data: varMap} + + issue, err := jiraClient.CreateTicket(ticket, campaign) if err != nil { ErrorAndExit(cmd, err) } - cmd.Printf("Ticket %s successfully created in JIRA", ticketKey) + cmd.Printf("Ticket %s successfully created in JIRA", issue.Key) return nil } diff --git a/jira/jira.go b/jira/jira.go index 4b44845..73f9e17 100644 --- a/jira/jira.go +++ b/jira/jira.go @@ -1,6 +1,13 @@ package jira import ( + "encoding/json" + "fmt" + "bytes" + "text/template" + + "git.ctrlz.es/mgdelacroix/campaigner/model" + jira "gopkg.in/andygrunwald/go-jira.v1" ) @@ -8,6 +15,64 @@ type JiraClient struct { *jira.Client } +func (c *JiraClient) GetIssueFromTicket(ticket *model.Ticket, campaign *model.Campaign) (*jira.Issue, error) { + summaryTmpl, err := template.New("").Parse(campaign.Summary) + if err != nil { + return nil, err + } + + var summaryBytes bytes.Buffer + if err := summaryTmpl.Execute(&summaryBytes, ticket.Data); err != nil { + return nil, err + } + summary := summaryBytes.String() + + descriptionTemplate, err := template.ParseFiles(campaign.Template) + if err != nil { + return nil, err + } + + var descriptionBytes bytes.Buffer + if err := descriptionTemplate.Execute(&descriptionBytes, ticket.Data); err != nil { + return nil, err + } + description := descriptionBytes.String() + + data := map[string]string{ + "Description": description, + "Summary": summary, + "Project": campaign.Project, + "Issue Type": "Story", + "Epic Link": campaign.Epic, + } + + if team, ok := ticket.Data["team"]; ok { + data["team"] = team.(string) + } + + createMetaInfo, _, err := c.Issue.GetCreateMeta(campaign.Project) + if err != nil { + return nil, err + } + + project := createMetaInfo.GetProjectWithKey(campaign.Project) + if project == nil { + return nil, fmt.Errorf("Error retrieving project with key %s", campaign.Project) + } + + issueType := project.GetIssueTypeWithName("Story") + if issueType == nil { + return nil, fmt.Errorf("Error retrieving issue type with name Story") + } + + issue, err := jira.InitIssueWithMetaAndFields(project, issueType, data) + if err != nil { + return nil, err + } + + return issue, nil +} + func NewClient(url, username, token string) (*JiraClient, error) { tp := jira.BasicAuthTransport{ Username: username, @@ -22,38 +87,21 @@ func NewClient(url, username, token string) (*JiraClient, error) { return &JiraClient{client}, nil } -func (c *JiraClient) CreateIssue(epicId, team, summary, description string) (string, error) { - /* - data := map[string]interface{}{ - "fields": map[string]interface{}{ - "project": map[string]interface{}{"key": "MM"}, - "summary": summary, - "description": description, - "issuetype": map[string]interface{}{"name": "Story"}, - "customfield_10007": epicId, - "customfield_11101": map[string]interface{}{"value": team}, - }, - } +func (c *JiraClient) CreateTicket(ticket *model.Ticket, campaign *model.Campaign) (*jira.Issue, error) { + issue, err := c.GetIssueFromTicket(ticket, campaign) + if err != nil { + return nil, err + } - body, err := json.Marshal(data) - if err != nil { - return "", err - } + b, _ := json.MarshalIndent(issue, "", " ") + fmt.Println(string(b)) - res, err := http.DoPost(c.Username, c.Token, c.Url+"issue/", body) - if err != nil { - return "", err - } - defer res.Body.Close() + newIssue, _, err := c.Issue.Create(issue) + if err != nil { + return nil, err + } - issue, err := IssueFromJson(res.Body) - if err != nil { - return "", err - } - - return issue.Key, nil - */ - return "", nil + return newIssue, nil } func (c *JiraClient) GetIssue(issueNo string) (*jira.Issue, error) { diff --git a/model/campaign.go b/model/campaign.go index 2860e2f..0bf6284 100644 --- a/model/campaign.go +++ b/model/campaign.go @@ -1,9 +1,11 @@ package model +// ToDo: add key-value extra params as a map to allow for customfield_whatever = team type Campaign struct { - Url string `json:"url"` - Project string `json:"project"` - Epic string `json:"epic"` - Summary string `json:"summary"` - Tickets []*Ticket `json:"tickets,omitempty"` + Url string `json:"url"` + Project string `json:"project"` + Epic string `json:"epic"` + Summary string `json:"summary"` + Template string `json:"template"` + Tickets []*Ticket `json:"tickets,omitempty"` }