Allow standalone command to create issues

This commit is contained in:
Miguel de la Cruz 2020-03-05 20:42:25 +01:00
parent ab2e28e29e
commit 1b66405f39
6 changed files with 148 additions and 93 deletions

View file

@ -23,7 +23,7 @@ func GrepAddCmd() *cobra.Command {
Run: grepAddCmdF, 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 return cmd
} }
@ -38,7 +38,7 @@ func AgAddCmd() *cobra.Command {
RunE: agAddCmdF, 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 return cmd
} }
@ -53,7 +53,7 @@ func GovetAddCmd() *cobra.Command {
RunE: govetAddCmdF, 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 return cmd
} }
@ -67,7 +67,7 @@ func CsvAddCmd() *cobra.Command {
RunE: csvAddCmdF, 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 return cmd
} }

View file

@ -15,14 +15,16 @@ func InitCmd() *cobra.Command {
Run: initCmdF, Run: initCmdF,
} }
cmd.Flags().StringP("url", "u", "", "the jira server URL") cmd.Flags().StringP("url", "u", "", "The jira server URL")
_ = cmd.MarkFlagRequired("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.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.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.MarkFlagRequired("summary")
cmd.Flags().StringP("template", "t", "", "The template path for the description of the tickets")
_ = cmd.MarkFlagRequired("template")
return cmd return cmd
} }
@ -32,12 +34,14 @@ func initCmdF(cmd *cobra.Command, _ []string) {
project, _ := cmd.Flags().GetString("project") project, _ := cmd.Flags().GetString("project")
epic, _ := cmd.Flags().GetString("epic") epic, _ := cmd.Flags().GetString("epic")
summary, _ := cmd.Flags().GetString("summary") summary, _ := cmd.Flags().GetString("summary")
template, _ := cmd.Flags().GetString("template")
cmp := &model.Campaign{ cmp := &model.Campaign{
Url: url, Url: url,
Project: project, Project: project,
Epic: epic, Epic: epic,
Summary: summary, Summary: summary,
Template: template,
} }
if err := campaign.Save(cmp); err != nil { if err := campaign.Save(cmp); err != nil {
ErrorAndExit(cmd, err) ErrorAndExit(cmd, err)

View file

@ -5,12 +5,17 @@ import (
) )
func JiraPublishCmd() *cobra.Command { func JiraPublishCmd() *cobra.Command {
return &cobra.Command{ cmd := &cobra.Command{
Use: "jira", Use: "jira",
Short: "Publishes the campaign tickets in JIRA", Short: "Publishes the campaign tickets in JIRA",
Args: cobra.NoArgs, 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 { func PublishCmd() *cobra.Command {
@ -26,4 +31,15 @@ func PublishCmd() *cobra.Command {
return cmd 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
}

View file

@ -1,13 +1,12 @@
package cmd package cmd
import ( import (
"bytes"
"fmt" "fmt"
"strings" "strings"
"text/template"
"git.ctrlz.es/mgdelacroix/campaigner/config" "git.ctrlz.es/mgdelacroix/campaigner/config"
"git.ctrlz.es/mgdelacroix/campaigner/jira" "git.ctrlz.es/mgdelacroix/campaigner/jira"
"git.ctrlz.es/mgdelacroix/campaigner/model"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -34,19 +33,19 @@ func CreateJiraTicketStandaloneCmd() *cobra.Command {
RunE: createJiraTicketStandaloneCmdF, RunE: createJiraTicketStandaloneCmdF,
} }
cmd.Flags().String("url", "", "the jira server URL") cmd.Flags().String("url", "", "The jira server URL")
_ = cmd.MarkFlagRequired("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.MarkFlagRequired("epic")
cmd.Flags().String("team", "", "the team for the new ticket") cmd.Flags().StringP("project", "p", "", "The jira project key to associate the tickets with")
_ = cmd.MarkFlagRequired("epic") _ = cmd.MarkFlagRequired("project")
cmd.Flags().String("username", "", "the jira username") cmd.Flags().String("summary", "", "The summary of the ticket")
cmd.Flags().String("token", "", "the jira token")
cmd.Flags().String("summary", "", "the summary of the ticket")
_ = cmd.MarkFlagRequired("summary") _ = 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.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 return cmd
} }
@ -59,16 +58,16 @@ func GetJiraTicketStandaloneCmd() *cobra.Command {
Run: getJiraTicketStandaloneCmdF, Run: getJiraTicketStandaloneCmdF,
} }
cmd.Flags().String("url", "", "the jira server URL") cmd.Flags().String("url", "", "The jira server URL")
_ = cmd.MarkFlagRequired("url") _ = cmd.MarkFlagRequired("url")
cmd.Flags().String("username", "", "the jira username") cmd.Flags().String("username", "", "The jira username")
cmd.Flags().String("token", "", "the jira token") cmd.Flags().String("token", "", "The jira token")
return cmd return cmd
} }
func getVarMap(vars []string) (map[string]string, error) { func getVarMap(vars []string) (map[string]interface{}, error) {
varMap := map[string]string{} varMap := map[string]interface{}{}
for _, v := range vars { for _, v := range vars {
parts := strings.Split(v, "=") parts := strings.Split(v, "=")
if len(parts) < 2 { if len(parts) < 2 {
@ -81,12 +80,12 @@ func getVarMap(vars []string) (map[string]string, error) {
func createJiraTicketStandaloneCmdF(cmd *cobra.Command, _ []string) error { func createJiraTicketStandaloneCmdF(cmd *cobra.Command, _ []string) error {
url, _ := cmd.Flags().GetString("url") url, _ := cmd.Flags().GetString("url")
epicId, _ := cmd.Flags().GetString("epic") epic, _ := cmd.Flags().GetString("epic")
team, _ := cmd.Flags().GetString("team") project, _ := cmd.Flags().GetString("project")
username, _ := cmd.Flags().GetString("username") username, _ := cmd.Flags().GetString("username")
token, _ := cmd.Flags().GetString("token") token, _ := cmd.Flags().GetString("token")
summaryTmplStr, _ := cmd.Flags().GetString("summary") summary, _ := cmd.Flags().GetString("summary")
templatePath, _ := cmd.Flags().GetString("template") template, _ := cmd.Flags().GetString("template")
vars, _ := cmd.Flags().GetStringSlice("vars") vars, _ := cmd.Flags().GetStringSlice("vars")
if username == "" || token == "" { if username == "" || token == "" {
@ -108,39 +107,25 @@ func createJiraTicketStandaloneCmdF(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("error processing vars: %w", err) 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) jiraClient, err := jira.NewClient(url, username, token)
if err != nil { if err != nil {
ErrorAndExit(cmd, err) 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 { if err != nil {
ErrorAndExit(cmd, err) ErrorAndExit(cmd, err)
} }
cmd.Printf("Ticket %s successfully created in JIRA", ticketKey) cmd.Printf("Ticket %s successfully created in JIRA", issue.Key)
return nil return nil
} }

View file

@ -1,6 +1,13 @@
package jira package jira
import ( import (
"encoding/json"
"fmt"
"bytes"
"text/template"
"git.ctrlz.es/mgdelacroix/campaigner/model"
jira "gopkg.in/andygrunwald/go-jira.v1" jira "gopkg.in/andygrunwald/go-jira.v1"
) )
@ -8,6 +15,64 @@ type JiraClient struct {
*jira.Client *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) { func NewClient(url, username, token string) (*JiraClient, error) {
tp := jira.BasicAuthTransport{ tp := jira.BasicAuthTransport{
Username: username, Username: username,
@ -22,38 +87,21 @@ func NewClient(url, username, token string) (*JiraClient, error) {
return &JiraClient{client}, nil return &JiraClient{client}, nil
} }
func (c *JiraClient) CreateIssue(epicId, team, summary, description string) (string, error) { func (c *JiraClient) CreateTicket(ticket *model.Ticket, campaign *model.Campaign) (*jira.Issue, error) {
/* issue, err := c.GetIssueFromTicket(ticket, campaign)
data := map[string]interface{}{ if err != nil {
"fields": map[string]interface{}{ return nil, err
"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},
},
}
body, err := json.Marshal(data) b, _ := json.MarshalIndent(issue, "", " ")
if err != nil { fmt.Println(string(b))
return "", err
}
res, err := http.DoPost(c.Username, c.Token, c.Url+"issue/", body) newIssue, _, err := c.Issue.Create(issue)
if err != nil { if err != nil {
return "", err return nil, err
} }
defer res.Body.Close()
issue, err := IssueFromJson(res.Body) return newIssue, nil
if err != nil {
return "", err
}
return issue.Key, nil
*/
return "", nil
} }
func (c *JiraClient) GetIssue(issueNo string) (*jira.Issue, error) { func (c *JiraClient) GetIssue(issueNo string) (*jira.Issue, error) {

View file

@ -1,9 +1,11 @@
package model package model
// ToDo: add key-value extra params as a map to allow for customfield_whatever = team
type Campaign struct { type Campaign struct {
Url string `json:"url"` Url string `json:"url"`
Project string `json:"project"` Project string `json:"project"`
Epic string `json:"epic"` Epic string `json:"epic"`
Summary string `json:"summary"` Summary string `json:"summary"`
Tickets []*Ticket `json:"tickets,omitempty"` Template string `json:"template"`
Tickets []*Ticket `json:"tickets,omitempty"`
} }