From 5a4e17cb49b40c84605fbd342f5e801b69b23a65 Mon Sep 17 00:00:00 2001 From: Cory Bennett Date: Sun, 21 Aug 2016 00:45:07 -0700 Subject: [PATCH] update for golint --- cli.go | 163 +++++++++++++------------ commands.go | 255 +++++++++++++++++++++------------------ data/AllowedValues.go | 19 +++ data/FieldMeta.go | 74 +++++++++++- data/FieldMetaMap.go | 74 +++++++++++- data/JsonType.go | 32 ++++- data/Operations.go | 21 ++++ data/Status.go | 60 ++++++++- data/StatusCategory.go | 30 ++++- data/Transition.go | 152 ++++++++++++++++++++++- data/Transitions.go | 156 +++++++++++++++++++++++- data/TransitionsFuncs.go | 3 + data/TransitionsMeta.go | 167 ++++++++++++++++++++++++- t/000setup.t | 8 +- templates.go | 58 ++++----- util.go | 77 ++++++------ 16 files changed, 1075 insertions(+), 274 deletions(-) create mode 100644 data/AllowedValues.go create mode 100644 data/Operations.go diff --git a/cli.go b/cli.go index ac954ab..3cb649d 100644 --- a/cli.go +++ b/cli.go @@ -23,10 +23,12 @@ import ( ) var ( - log = logging.MustGetLogger("jira") + log = logging.MustGetLogger("jira") + // VERSION is the go-jira library version VERSION string ) +// Cli is go-jira client object type Cli struct { endpoint *url.URL opts map[string]interface{} @@ -34,6 +36,7 @@ type Cli struct { ua *http.Client } +// New creates go-jira client object func New(opts map[string]interface{}) *Cli { homedir := homedir() cookieJar, _ := cookiejar.New(nil) @@ -120,7 +123,7 @@ func (c *Cli) loadCookies() []*http.Cookie { log.Errorf("Failed to open %s: %s", c.cookieFile, err) panic(err) } - cookies := make([]*http.Cookie, 0) + cookies := []*http.Cookie{} err = json.Unmarshal(bytes, &cookies) if err != nil { log.Errorf("Failed to parse json from file %s: %s", c.cookieFile, err) @@ -140,44 +143,42 @@ func (c *Cli) put(uri string, content string) (*http.Response, error) { return c.makeRequestWithContent("PUT", uri, content) } -func (c *Cli) delete(uri string) (*http.Response, error) { +func (c *Cli) delete(uri string) (resp *http.Response, err error) { method := "DELETE" req, _ := http.NewRequest(method, uri, nil) log.Infof("%s %s", req.Method, req.URL.String()) - if resp, err := c.makeRequest(req); err != nil { + if resp, err = c.makeRequest(req); err != nil { return nil, err - } else { - if resp.StatusCode == 401 { - if err := c.CmdLogin(); err != nil { - return nil, err - } - req, _ = http.NewRequest(method, uri, nil) - return c.makeRequest(req) - } - return resp, err } + if resp.StatusCode == 401 { + if err = c.CmdLogin(); err != nil { + return nil, err + } + req, _ = http.NewRequest(method, uri, nil) + return c.makeRequest(req) + } + return resp, err } -func (c *Cli) makeRequestWithContent(method string, uri string, content string) (*http.Response, error) { +func (c *Cli) makeRequestWithContent(method string, uri string, content string) (resp *http.Response, err error) { buffer := bytes.NewBufferString(content) req, _ := http.NewRequest(method, uri, buffer) log.Infof("%s %s", req.Method, req.URL.String()) - if resp, err := c.makeRequest(req); err != nil { + if resp, err = c.makeRequest(req); err != nil { return nil, err - } else { - if resp.StatusCode == 401 { - if err := c.CmdLogin(); err != nil { - return nil, err - } - req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content)) - return c.makeRequest(req) - } - return resp, err } + if resp.StatusCode == 401 { + if err = c.CmdLogin(); err != nil { + return nil, err + } + req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content)) + return c.makeRequest(req) + } + return resp, err } -func (c *Cli) get(uri string) (*http.Response, error) { +func (c *Cli) get(uri string) (resp *http.Response, err error) { req, _ := http.NewRequest("GET", uri, nil) log.Infof("%s %s", req.Method, req.URL.String()) if log.IsEnabledFor(logging.DEBUG) { @@ -186,17 +187,16 @@ func (c *Cli) get(uri string) (*http.Response, error) { log.Debugf("%s", logBuffer) } - if resp, err := c.makeRequest(req); err != nil { + if resp, err = c.makeRequest(req); err != nil { return nil, err - } else { - if resp.StatusCode == 401 { - if err := c.CmdLogin(); err != nil { - return nil, err - } - return c.makeRequest(req) - } - return resp, err } + if resp.StatusCode == 401 { + if err := c.CmdLogin(); err != nil { + return nil, err + } + return c.makeRequest(req) + } + return resp, err } func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) { @@ -217,18 +217,17 @@ func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) { if resp, err = c.ua.Do(req); err != nil { log.Errorf("Failed to %s %s: %s", req.Method, req.URL.String(), err) return nil, err - } else { - if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 { - log.Errorf("response status: %s", resp.Status) - } + } + if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 { + log.Errorf("response status: %s", resp.Status) + } - runtime.SetFinalizer(resp, func(r *http.Response) { - r.Body.Close() - }) + runtime.SetFinalizer(resp, func(r *http.Response) { + r.Body.Close() + }) - if _, ok := resp.Header["Set-Cookie"]; ok { - c.saveCookies(resp) - } + if _, ok := resp.Header["Set-Cookie"]; ok { + c.saveCookies(resp) } if log.IsEnabledFor(logging.DEBUG) { out, _ := httputil.DumpResponse(resp, true) @@ -237,6 +236,7 @@ func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) { return resp, nil } +// GetTemplate will return the text/template for the given command name func (c *Cli) GetTemplate(name string) string { return c.getTemplate(name) } @@ -256,10 +256,9 @@ func (c *Cli) getTemplate(name string) string { if override, ok := c.opts["template"].(string); ok { if _, err := os.Stat(override); err == nil { return readFile(override) - } else { - if t := getLookedUpTemplate(override, all_templates[override]); t != "" { - return t - } + } + if t := getLookedUpTemplate(override, allTemplates[override]); t != "" { + return t } } // create-bug etc are special, if we dont find it in the path @@ -267,9 +266,11 @@ func (c *Cli) getTemplate(name string) string { if strings.HasPrefix(name, "create-") { return getLookedUpTemplate(name, c.getTemplate("create")) } - return getLookedUpTemplate(name, all_templates[name]) + return getLookedUpTemplate(name, allTemplates[name]) } +// NoChangesFound is an error returned from when editing templates +// and no modifications were made while editing type NoChangesFound struct{} func (f NoChangesFound) Error() string { @@ -360,27 +361,27 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m } edited := make(map[string]interface{}) - if fh, err := ioutil.ReadFile(tmpFileName); err != nil { + var data []byte + if data, err = ioutil.ReadFile(tmpFileName); err != nil { log.Errorf("Failed to read tmpfile %s: %s", tmpFileName, err) if editing && promptYN("edit again?", true) { continue } return err - } else { - if err := yaml.Unmarshal(fh, &edited); err != nil { - log.Errorf("Failed to parse YAML: %s", err) - if editing && promptYN("edit again?", true) { - continue - } - return err + } + if err := yaml.Unmarshal(data, &edited); err != nil { + log.Errorf("Failed to parse YAML: %s", err) + if editing && promptYN("edit again?", true) { + continue } + return err } - if fixed, err := yamlFixup(edited); err != nil { + var fixed interface{} + if fixed, err = yamlFixup(edited); err != nil { return err - } else { - edited = fixed.(map[string]interface{}) } + edited = fixed.(map[string]interface{}) // if you want to abort editing a jira issue then // you can add the "abort: true" flag to the document @@ -422,6 +423,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m return nil } +// Browse will open up your default browser to the provided issue func (c *Cli) Browse(issue string) error { if val, ok := c.opts["browse"].(bool); ok && val { if runtime.GOOS == "darwin" { @@ -433,6 +435,7 @@ func (c *Cli) Browse(issue string) error { return nil } +// SaveData will write out the yaml formated --saveFile file with provided data func (c *Cli) SaveData(data interface{}) error { if val, ok := c.opts["saveFile"].(string); ok && val != "" { yamlWrite(val, data) @@ -440,33 +443,39 @@ func (c *Cli) SaveData(data interface{}) error { return nil } +// ViewIssue will return the details for the given issue id func (c *Cli) ViewIssue(issue string) (interface{}, error) { uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue) if x := c.expansions(); len(x) > 0 { uri = fmt.Sprintf("%s?expand=%s", uri, strings.Join(x, ",")) } - data, err := responseToJson(c.get(uri)) + data, err := responseToJSON(c.get(uri)) if err != nil { return nil, err - } else { - return data, nil } + return data, nil } +// FindIssues will return a list of issues that match the given options. +// If the "query" option is undefined it will generate a JQL query +// using any/all of the provide options: project, component, assignee, +// issuetype, watcher, reporter, sort +// Further it will restrict the fields being extracted from the jira +// response with the 'queryfields' option func (c *Cli) FindIssues() (interface{}, error) { var query string var ok bool // project = BAKERY and status not in (Resolved, Closed) if query, ok = c.opts["query"].(string); !ok { qbuff := bytes.NewBufferString("resolution = unresolved") - if project, ok := c.opts["project"]; !ok { + var project string + if project, ok = c.opts["project"].(string); !ok { err := fmt.Errorf("Missing required arguments, either 'query' or 'project' are required") log.Errorf("%s", err) return nil, err - } else { - qbuff.WriteString(fmt.Sprintf(" AND project = '%s'", project)) } + qbuff.WriteString(fmt.Sprintf(" AND project = '%s'", project)) if component, ok := c.opts["component"]; ok { qbuff.WriteString(fmt.Sprintf(" AND component = '%s'", component)) @@ -495,11 +504,9 @@ func (c *Cli) FindIssues() (interface{}, error) { query = qbuff.String() } - fields := make([]string, 0) + fields := []string{"summary"} if qf, ok := c.opts["queryfields"].(string); ok { fields = strings.Split(qf, ",") - } else { - fields = append(fields, "summary") } json, err := jsonEncode(map[string]interface{}{ @@ -514,13 +521,15 @@ func (c *Cli) FindIssues() (interface{}, error) { } uri := fmt.Sprintf("%s/rest/api/2/search", c.endpoint) - if data, err := responseToJson(c.post(uri, json)); err != nil { + var data interface{} + if data, err = responseToJSON(c.post(uri, json)); err != nil { return nil, err - } else { - return data, nil } + return data, nil } +// GetOptString will extract the string from the Cli object options +// otherwise return the provided default func (c *Cli) GetOptString(optName string, dflt string) string { return c.getOptString(optName, dflt) } @@ -528,11 +537,12 @@ func (c *Cli) GetOptString(optName string, dflt string) string { func (c *Cli) getOptString(optName string, dflt string) string { if val, ok := c.opts[optName].(string); ok { return val - } else { - return dflt } + return dflt } +// GetOptBool will extract the boolean value from the Client object options +// otherwise return the provided default\ func (c *Cli) GetOptBool(optName string, dflt bool) bool { return c.getOptBool(optName, dflt) } @@ -540,14 +550,13 @@ func (c *Cli) GetOptBool(optName string, dflt bool) bool { func (c *Cli) getOptBool(optName string, dflt bool) bool { if val, ok := c.opts[optName].(bool); ok { return val - } else { - return dflt } + return dflt } // expansions returns a comma-separated list of values for field expansion func (c *Cli) expansions() []string { - expansions := make([]string, 0) + var expansions []string if x, ok := c.opts["expand"].(string); ok { expansions = strings.Split(x, ",") } diff --git a/commands.go b/commands.go index e0d1e0f..7823dca 100644 --- a/commands.go +++ b/commands.go @@ -3,7 +3,6 @@ package jira import ( "bytes" "encoding/json" - "errors" "fmt" "github.com/Netflix-Skunkworks/go-jira/data" "github.com/howeyc/gopass" @@ -15,9 +14,10 @@ import ( // "github.com/kr/pretty" ) +// CmdLogin will attempt to login into jira server func (c *Cli) CmdLogin() error { uri := fmt.Sprintf("%s/rest/auth/1/session", c.endpoint) - for true { + for { req, _ := http.NewRequest("GET", uri, nil) user, _ := c.opts["user"].(string) @@ -29,60 +29,63 @@ func (c *Cli) CmdLogin() error { passwd := string(pw) req.SetBasicAuth(user, passwd) - if resp, err := c.makeRequest(req); err != nil { + + resp, err := c.makeRequest(req) + if err != nil { return err - } else { - if resp.StatusCode == 403 { - // probably got this, need to redirect the user to login manually - // X-Authentication-Denied-Reason: CAPTCHA_CHALLENGE; login-url=https://jira/login.jsp - if reason := resp.Header.Get("X-Authentication-Denied-Reason"); reason != "" { - err := fmt.Errorf("Authenticaion Failed: %s", reason) - log.Errorf("%s", err) - return err - } - err := fmt.Errorf("Authentication Failed: Unknown Reason") + } + if resp.StatusCode == 403 { + // probably got this, need to redirect the user to login manually + // X-Authentication-Denied-Reason: CAPTCHA_CHALLENGE; login-url=https://jira/login.jsp + if reason := resp.Header.Get("X-Authentication-Denied-Reason"); reason != "" { + err := fmt.Errorf("Authenticaion Failed: %s", reason) log.Errorf("%s", err) return err + } + err := fmt.Errorf("Authentication Failed: Unknown Reason") + log.Errorf("%s", err) + return err - } else if resp.StatusCode == 200 { - // https://confluence.atlassian.com/display/JIRA043/JIRA+REST+API+%28Alpha%29+Tutorial#JIRARESTAPI%28Alpha%29Tutorial-CAPTCHAs - // probably bad password, try again - if reason := resp.Header.Get("X-Seraph-Loginreason"); reason == "AUTHENTICATION_DENIED" { - log.Warning("Authentication Failed: %s", reason) - continue - } - } else { - log.Warning("Login failed") + } else if resp.StatusCode == 200 { + // https://confluence.atlassian.com/display/JIRA043/JIRA+REST+API+%28Alpha%29+Tutorial#JIRARESTAPI%28Alpha%29Tutorial-CAPTCHAs + // probably bad password, try again + if reason := resp.Header.Get("X-Seraph-Loginreason"); reason == "AUTHENTICATION_DENIED" { + log.Warning("Authentication Failed: %s", reason) continue } + break + } else { + log.Warning("Login failed") + continue } - return nil } return nil } +// CmdLogout will close any active sessions func (c *Cli) CmdLogout() error { uri := fmt.Sprintf("%s/rest/auth/1/session", c.endpoint) req, _ := http.NewRequest("DELETE", uri, nil) - if resp, err := c.makeRequest(req); err != nil { + resp, err := c.makeRequest(req) + if err != nil { return err + } + if resp.StatusCode == 401 || resp.StatusCode == 204 { + // 401 == no active session + // 204 == successfully logged out } else { - if resp.StatusCode == 401 || resp.StatusCode == 204 { - // 401 == no active session - // 204 == successfully logged out - } else { - err := fmt.Errorf("Failed to Logout: %s", err) - return err - } + err := fmt.Errorf("Failed to Logout: %s", err) + return err } log.Notice("OK") return nil } +// CmdFields will send data from /rest/api/2/field API to "fields" template func (c *Cli) CmdFields() error { log.Debugf("fields called") uri := fmt.Sprintf("%s/rest/api/2/field", c.endpoint) - data, err := responseToJson(c.get(uri)) + data, err := responseToJSON(c.get(uri)) if err != nil { return err } @@ -90,15 +93,17 @@ func (c *Cli) CmdFields() error { return runTemplate(c.getTemplate("fields"), data, nil) } +// CmdList will query jira and send data to "list" template func (c *Cli) CmdList() error { log.Debugf("list called") - if data, err := c.FindIssues(); err != nil { + data, err := c.FindIssues() + if err != nil { return err - } else { - return runTemplate(c.getTemplate("list"), data, nil) } + return runTemplate(c.getTemplate("list"), data, nil) } +// CmdView will get issue data and send to "view" template func (c *Cli) CmdView(issue string) error { log.Debugf("view called") c.Browse(issue) @@ -109,23 +114,23 @@ func (c *Cli) CmdView(issue string) error { return runTemplate(c.getTemplate("view"), data, nil) } +// CmdEdit will populate "edit" template with issue data and issue "editmeta" data. +// Then will parse yaml template and submit data to jira. func (c *Cli) CmdEdit(issue string) error { log.Debugf("edit called") uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, issue) - editmeta, err := responseToJson(c.get(uri)) + editmeta, err := responseToJSON(c.get(uri)) if err != nil { return err } uri = fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue) - var issueData map[string]interface{} - if data, err := responseToJson(c.get(uri)); err != nil { + data, err := responseToJSON(c.get(uri)) + if err != nil { return err - } else { - issueData = data.(map[string]interface{}) } - + issueData := data.(map[string]interface{}) issueData["meta"] = editmeta.(map[string]interface{}) issueData["overrides"] = c.opts @@ -150,22 +155,22 @@ func (c *Cli) CmdEdit(issue string) error { fmt.Printf("OK %s %s/browse/%s\n", issueData["key"], c.endpoint, issueData["key"]) } return nil - } else { - logBuffer := bytes.NewBuffer(make([]byte, 0)) - resp.Write(logBuffer) - err := fmt.Errorf("Unexpected Response From PUT") - log.Errorf("%s:\n%s", err, logBuffer) - return err } + logBuffer := bytes.NewBuffer(make([]byte, 0)) + resp.Write(logBuffer) + err = fmt.Errorf("Unexpected Response From PUT") + log.Errorf("%s:\n%s", err, logBuffer) + return err }, ) } +// CmdEditMeta will send issue 'edit' metadata to the "editmeta" template func (c *Cli) CmdEditMeta(issue string) error { log.Debugf("editMeta called") c.Browse(issue) uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, issue) - data, err := responseToJson(c.get(uri)) + data, err := responseToJSON(c.get(uri)) if err != nil { return err } @@ -173,11 +178,12 @@ func (c *Cli) CmdEditMeta(issue string) error { return runTemplate(c.getTemplate("editmeta"), data, nil) } +// CmdTransitionMeta will send available transition metadata to the "transmeta" template func (c *Cli) CmdTransitionMeta(issue string) error { log.Debugf("tranisionMeta called") c.Browse(issue) uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, issue) - data, err := responseToJson(c.get(uri)) + data, err := responseToJSON(c.get(uri)) if err != nil { return err } @@ -185,11 +191,12 @@ func (c *Cli) CmdTransitionMeta(issue string) error { return runTemplate(c.getTemplate("transmeta"), data, nil) } +// CmdIssueTypes will send issue 'create' metadata to the 'issuetypes' func (c *Cli) CmdIssueTypes() error { project := c.opts["project"].(string) log.Debugf("issueTypes called") uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s", c.endpoint, project) - data, err := responseToJson(c.get(uri)) + data, err := responseToJSON(c.get(uri)) if err != nil { return err } @@ -200,7 +207,7 @@ func (c *Cli) CmdIssueTypes() error { func (c *Cli) defaultIssueType() string { project := c.opts["project"].(string) uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s", c.endpoint, project) - data, _ := responseToJson(c.get(uri)) + data, _ := responseToJSON(c.get(uri)) issueTypeNames := make(map[string]bool) if data, ok := data.(map[string]interface{}); ok { @@ -226,6 +233,7 @@ func (c *Cli) defaultIssueType() string { return "" } +// CmdCreateMeta sends the 'create' metadata for the given project & issuetype and sends it to the 'createmeta' template func (c *Cli) CmdCreateMeta() error { project := c.opts["project"].(string) issuetype := c.getOptString("issuetype", "") @@ -235,7 +243,7 @@ func (c *Cli) CmdCreateMeta() error { log.Debugf("createMeta called") uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, url.QueryEscape(issuetype)) - data, err := responseToJson(c.get(uri)) + data, err := responseToJSON(c.get(uri)) if err != nil { return err } @@ -254,16 +262,18 @@ func (c *Cli) CmdCreateMeta() error { return runTemplate(c.getTemplate("createmeta"), data, nil) } +// CmdComponents sends component data for given project and sends to the "components" template func (c *Cli) CmdComponents(project string) error { log.Debugf("Components called") uri := fmt.Sprintf("%s/rest/api/2/project/%s/components", c.endpoint, project) - data, err := responseToJson(c.get(uri)) + data, err := responseToJSON(c.get(uri)) if err != nil { return err } return runTemplate(c.getTemplate("components"), data, nil) } +// ValidTransitions will return a list of valid transitions for given issue. func (c *Cli) ValidTransitions(issue string) (jiradata.Transitions, error) { uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, issue) resp, err := c.get(uri) @@ -284,18 +294,21 @@ func (c *Cli) ValidTransitions(issue string) (jiradata.Transitions, error) { return transMeta.Transitions, nil } +// CmdTransitions sends valid transtions for given issue to the "transitions" template func (c *Cli) CmdTransitions(issue string) error { log.Debugf("Transitions called") // FIXME this should just call ValidTransitions then pass that data to templates c.Browse(issue) uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue) - data, err := responseToJson(c.get(uri)) + data, err := responseToJSON(c.get(uri)) if err != nil { return err } return runTemplate(c.getTemplate("transitions"), data, nil) } +// CmdCreate sends the create-metadata to the "create" template for editing, then +// will parse the edited document as YAML and submit the document to jira. func (c *Cli) CmdCreate() error { log.Debugf("create called") project := c.opts["project"].(string) @@ -305,7 +318,7 @@ func (c *Cli) CmdCreate() error { } uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, url.QueryEscape(issuetype)) - data, err := responseToJson(c.get(uri)) + data, err := responseToJSON(c.get(uri)) if err != nil { return err } @@ -349,42 +362,43 @@ func (c *Cli) CmdCreate() error { if resp.StatusCode == 201 { // response: {"id":"410836","key":"PROJ-238","self":"https://jira/rest/api/2/issue/410836"} - if json, err := responseToJson(resp, nil); err != nil { + json, err := responseToJSON(resp, nil) + if err != nil { return err - } else { - key := json.(map[string]interface{})["key"].(string) - link := fmt.Sprintf("%s/browse/%s", c.endpoint, key) - c.Browse(key) - c.SaveData(map[string]string{ - "issue": key, - "link": link, - }) - if !c.opts["quiet"].(bool) { - fmt.Printf("OK %s %s\n", key, link) - } + } + key := json.(map[string]interface{})["key"].(string) + link := fmt.Sprintf("%s/browse/%s", c.endpoint, key) + c.Browse(key) + c.SaveData(map[string]string{ + "issue": key, + "link": link, + }) + if !c.opts["quiet"].(bool) { + fmt.Printf("OK %s %s\n", key, link) } return nil - } else { - logBuffer := bytes.NewBuffer(make([]byte, 0)) - resp.Write(logBuffer) - err := fmt.Errorf("Unexpected Response From POST") - log.Errorf("%s:\n%s", err, logBuffer) - return err } + logBuffer := bytes.NewBuffer(make([]byte, 0)) + resp.Write(logBuffer) + err = fmt.Errorf("Unexpected Response From POST") + log.Errorf("%s:\n%s", err, logBuffer) + return err }, ) } +// CmdIssueLinkTypes will send the issue link type data to the "issuelinktypes" template. func (c *Cli) CmdIssueLinkTypes() error { log.Debugf("Transitions called") uri := fmt.Sprintf("%s/rest/api/2/issueLinkType", c.endpoint) - data, err := responseToJson(c.get(uri)) + data, err := responseToJSON(c.get(uri)) if err != nil { return err } return runTemplate(c.getTemplate("issuelinktypes"), data, nil) } +// CmdBlocks will update the given issue as being "blocked" by the given blocker func (c *Cli) CmdBlocks(blocker string, issue string) error { log.Debugf("blocks called") @@ -428,6 +442,8 @@ func (c *Cli) CmdBlocks(blocker string, issue string) error { return nil } +// CmdDups will update the given issue as being a duplicate by the given dup issue +// and will attempt to resolve the dup issue func (c *Cli) CmdDups(duplicate string, issue string) error { log.Debugf("dups called") @@ -471,6 +487,8 @@ func (c *Cli) CmdDups(duplicate string, issue string) error { return nil } +// CmdWatch will add the given watcher to the issue (or remove the watcher +// given the 'remove' flag) func (c *Cli) CmdWatch(issue string, watcher string, remove bool) error { log.Debugf("watch called: watcher: %q, remove: %n", watcher, remove) @@ -521,6 +539,7 @@ func (c *Cli) CmdWatch(issue string, watcher string, remove bool) error { return nil } +// CmdVote will add or remove a vote on an issue func (c *Cli) CmdVote(issue string, up bool) error { log.Debugf("vote called, with up: %n", up) @@ -564,16 +583,17 @@ func (c *Cli) CmdVote(issue string, up bool) error { return nil } +// CmdTransition will move state of the given issue to the given transtion func (c *Cli) CmdTransition(issue string, trans string) error { log.Debugf("transition called") uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, issue) - data, err := responseToJson(c.get(uri)) + data, err := responseToJSON(c.get(uri)) if err != nil { return err } transitions := data.(map[string]interface{})["transitions"].([]interface{}) - var transId, transName string + var transID, transName string var transMeta map[string]interface{} found := make([]string, 0, len(transitions)) for _, transition := range transitions { @@ -582,11 +602,11 @@ func (c *Cli) CmdTransition(issue string, trans string) error { found = append(found, name) if strings.Contains(strings.ToLower(name), strings.ToLower(trans)) { transName = name - transId = id + transID = id transMeta = transition.(map[string]interface{}) } } - if transId == "" { + if transID == "" { err := fmt.Errorf("Invalid Transition '%s', Available: %s", trans, strings.Join(found, ", ")) log.Debugf("%s", err) return err @@ -619,12 +639,11 @@ func (c *Cli) CmdTransition(issue string, trans string) error { } uri = fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue) - var issueData map[string]interface{} - if data, err := responseToJson(c.get(uri)); err != nil { + data, err = responseToJSON(c.get(uri)) + if err != nil { return err - } else { - issueData = data.(map[string]interface{}) } + issueData := data.(map[string]interface{}) issueData["meta"] = transMeta if c.GetOptString("defaultResolution", "") == "" { // .meta.fields.resolution.allowedValues @@ -647,7 +666,7 @@ func (c *Cli) CmdTransition(issue string, trans string) error { issueData["overrides"] = c.opts issueData["transition"] = map[string]interface{}{ "name": transName, - "id": transId, + "id": transID, } return c.editTemplate( c.getTemplate("transition"), @@ -657,6 +676,8 @@ func (c *Cli) CmdTransition(issue string, trans string) error { ) } +// CmdComment will open up editor with "comment" template and submit +// YAML output to jira func (c *Cli) CmdComment(issue string) error { log.Debugf("comment called") @@ -678,13 +699,12 @@ func (c *Cli) CmdComment(issue string) error { fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue) } return nil - } else { - logBuffer := bytes.NewBuffer(make([]byte, 0)) - resp.Write(logBuffer) - err := fmt.Errorf("Unexpected Response From POST") - log.Errorf("%s:\n%s", err, logBuffer) - return err } + logBuffer := bytes.NewBuffer(make([]byte, 0)) + resp.Write(logBuffer) + err = fmt.Errorf("Unexpected Response From POST") + log.Errorf("%s:\n%s", err, logBuffer) + return err } if comment, ok := c.opts["comment"]; ok && comment != "" { @@ -695,23 +715,23 @@ func (c *Cli) CmdComment(issue string) error { return err } return handlePost(json) - } else { - return c.editTemplate( - c.getTemplate("comment"), - fmt.Sprintf("%s-create-", issue), - map[string]interface{}{}, - handlePost, - ) } + return c.editTemplate( + c.getTemplate("comment"), + fmt.Sprintf("%s-create-", issue), + map[string]interface{}{}, + handlePost, + ) } +// CmdComponent will add a new component to given project func (c *Cli) CmdComponent(action string, project string, name string, desc string, lead string) error { log.Debugf("component called") switch action { case "add": default: - return errors.New(fmt.Sprintf("CmdComponent: %q is not a valid action", action)) + return fmt.Errorf("CmdComponent: %q is not a valid action", action) } json, err := jsonEncode(map[string]interface{}{ @@ -748,6 +768,7 @@ func (c *Cli) CmdComponent(action string, project string, name string, desc stri return nil } +// CmdLabels will add, remove or set labels on a given issue func (c *Cli) CmdLabels(action string, issue string, labels []string) error { log.Debugf("label called") @@ -773,23 +794,22 @@ func (c *Cli) CmdLabels(action string, issue string, labels []string) error { fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue) } return nil - } else { - logBuffer := bytes.NewBuffer(make([]byte, 0)) - resp.Write(logBuffer) - err := fmt.Errorf("Unexpected Response From PUT") - log.Errorf("%s:\n%s", err, logBuffer) - return err } + logBuffer := bytes.NewBuffer(make([]byte, 0)) + resp.Write(logBuffer) + err = fmt.Errorf("Unexpected Response From PUT") + log.Errorf("%s:\n%s", err, logBuffer) + return err } - var labels_json string + var labelsJSON string var err error if action == "set" { labelsActions := make([]map[string][]string, 1) labelsActions[0] = map[string][]string{ "set": labels, } - labels_json, err = jsonEncode(map[string]interface{}{ + labelsJSON, err = jsonEncode(map[string]interface{}{ "labels": labelsActions, }) } else { @@ -800,18 +820,19 @@ func (c *Cli) CmdLabels(action string, issue string, labels []string) error { } labelsActions[i] = labelActionMap } - labels_json, err = jsonEncode(map[string]interface{}{ + labelsJSON, err = jsonEncode(map[string]interface{}{ "labels": labelsActions, }) } if err != nil { return err } - json := fmt.Sprintf("{ \"update\": %s }", labels_json) + json := fmt.Sprintf("{ \"update\": %s }", labelsJSON) return handlePut(json) } +// CmdAssign will assign the given user to be the owner of the given issue func (c *Cli) CmdAssign(issue string, user string) error { log.Debugf("assign called") @@ -847,13 +868,14 @@ func (c *Cli) CmdAssign(issue string, user string) error { return nil } +// CmdExportTemplates will export the default templates to the template directory. func (c *Cli) CmdExportTemplates() error { dir := c.opts["directory"].(string) if err := mkdir(dir); err != nil { return err } - for name, template := range all_templates { + for name, template := range allTemplates { if wanted, ok := c.opts["template"]; ok && wanted != name { continue } @@ -862,18 +884,19 @@ func (c *Cli) CmdExportTemplates() error { log.Warning("Skipping %s, already exists", templateFile) continue } - if fh, err := os.OpenFile(templateFile, os.O_WRONLY|os.O_CREATE, 0644); err != nil { + fh, err := os.OpenFile(templateFile, os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { log.Errorf("Failed to open %s for writing: %s", templateFile, err) return err - } else { - defer fh.Close() - log.Noticef("Creating %s", templateFile) - fh.Write([]byte(template)) } + defer fh.Close() + log.Noticef("Creating %s", templateFile) + fh.Write([]byte(template)) } return nil } +// CmdRequest will use the given uri to make a request and potentially send provided content. func (c *Cli) CmdRequest(uri, content string) (err error) { log.Debugf("request called") @@ -884,11 +907,11 @@ func (c *Cli) CmdRequest(uri, content string) (err error) { method := strings.ToUpper(c.opts["method"].(string)) var data interface{} if method == "GET" { - data, err = responseToJson(c.get(uri)) + data, err = responseToJSON(c.get(uri)) } else if method == "POST" { - data, err = responseToJson(c.post(uri, content)) + data, err = responseToJSON(c.post(uri, content)) } else if method == "PUT" { - data, err = responseToJson(c.put(uri, content)) + data, err = responseToJSON(c.put(uri, content)) } if err != nil { return err diff --git a/data/AllowedValues.go b/data/AllowedValues.go new file mode 100644 index 0000000..f5a27a2 --- /dev/null +++ b/data/AllowedValues.go @@ -0,0 +1,19 @@ +package jiradata + +///////////////////////////////////////////////////////////////////////// +// This Code is Generated by SlipScheme Project: +// https://github.com/coryb/slipscheme +// +// Generated with command: +// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json +///////////////////////////////////////////////////////////////////////// +// DO NOT EDIT // +///////////////////////////////////////////////////////////////////////// + +// AllowedValues defined from schema: +// { +// "title": "allowedValues", +// "type": "array", +// "items": {} +// } +type AllowedValues []interface{} diff --git a/data/FieldMeta.go b/data/FieldMeta.go index cbe321f..9e46d33 100644 --- a/data/FieldMeta.go +++ b/data/FieldMeta.go @@ -4,18 +4,84 @@ package jiradata // This Code is Generated by SlipScheme Project: // https://github.com/coryb/slipscheme // -// Generated with command: slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json +// Generated with command: +// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json ///////////////////////////////////////////////////////////////////////// // DO NOT EDIT // ///////////////////////////////////////////////////////////////////////// +// FieldMeta defined from schema: +// { +// "title": "Field Meta", +// "type": "object", +// "properties": { +// "allowedValues": { +// "title": "allowedValues", +// "type": "array", +// "items": {} +// }, +// "autoCompleteUrl": { +// "title": "autoCompleteUrl", +// "type": "string" +// }, +// "hasDefaultValue": { +// "title": "hasDefaultValue", +// "type": "boolean" +// }, +// "key": { +// "title": "key", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "operations": { +// "title": "operations", +// "type": "array", +// "items": { +// "type": "string" +// } +// }, +// "required": { +// "title": "required", +// "type": "boolean" +// }, +// "schema": { +// "title": "Json Type", +// "type": "object", +// "properties": { +// "custom": { +// "title": "custom", +// "type": "string" +// }, +// "customId": { +// "title": "customId", +// "type": "integer" +// }, +// "items": { +// "title": "items", +// "type": "string" +// }, +// "system": { +// "title": "system", +// "type": "string" +// }, +// "type": { +// "title": "type", +// "type": "string" +// } +// } +// } +// } +// } type FieldMeta struct { - AllowedValues []interface{} `json:"allowedValues,omitempty" yaml:"allowedValues,omitempty"` + AllowedValues AllowedValues `json:"allowedValues,omitempty" yaml:"allowedValues,omitempty"` AutoCompleteURL string `json:"autoCompleteUrl,omitempty" yaml:"autoCompleteUrl,omitempty"` HasDefaultValue bool `json:"hasDefaultValue,omitempty" yaml:"hasDefaultValue,omitempty"` Key string `json:"key,omitempty" yaml:"key,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"` - Operations []string `json:"operations,omitempty" yaml:"operations,omitempty"` + Operations Operations `json:"operations,omitempty" yaml:"operations,omitempty"` Required bool `json:"required,omitempty" yaml:"required,omitempty"` - Schema *JsonType `json:"schema,omitempty" yaml:"schema,omitempty"` + Schema *JSONType `json:"schema,omitempty" yaml:"schema,omitempty"` } diff --git a/data/FieldMetaMap.go b/data/FieldMetaMap.go index 9a88f6a..91a6b39 100644 --- a/data/FieldMetaMap.go +++ b/data/FieldMetaMap.go @@ -4,9 +4,81 @@ package jiradata // This Code is Generated by SlipScheme Project: // https://github.com/coryb/slipscheme // -// Generated with command: slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json +// Generated with command: +// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json ///////////////////////////////////////////////////////////////////////// // DO NOT EDIT // ///////////////////////////////////////////////////////////////////////// +// FieldMetaMap defined from schema: +// { +// "title": "fields", +// "type": "object", +// "patternProperties": { +// ".+": { +// "title": "Field Meta", +// "type": "object", +// "properties": { +// "allowedValues": { +// "title": "allowedValues", +// "type": "array", +// "items": {} +// }, +// "autoCompleteUrl": { +// "title": "autoCompleteUrl", +// "type": "string" +// }, +// "hasDefaultValue": { +// "title": "hasDefaultValue", +// "type": "boolean" +// }, +// "key": { +// "title": "key", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "operations": { +// "title": "operations", +// "type": "array", +// "items": { +// "type": "string" +// } +// }, +// "required": { +// "title": "required", +// "type": "boolean" +// }, +// "schema": { +// "title": "Json Type", +// "type": "object", +// "properties": { +// "custom": { +// "title": "custom", +// "type": "string" +// }, +// "customId": { +// "title": "customId", +// "type": "integer" +// }, +// "items": { +// "title": "items", +// "type": "string" +// }, +// "system": { +// "title": "system", +// "type": "string" +// }, +// "type": { +// "title": "type", +// "type": "string" +// } +// } +// } +// } +// } +// } +// } type FieldMetaMap map[string]*FieldMeta diff --git a/data/JsonType.go b/data/JsonType.go index 3738f01..7dac1fe 100644 --- a/data/JsonType.go +++ b/data/JsonType.go @@ -4,12 +4,40 @@ package jiradata // This Code is Generated by SlipScheme Project: // https://github.com/coryb/slipscheme // -// Generated with command: slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json +// Generated with command: +// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json ///////////////////////////////////////////////////////////////////////// // DO NOT EDIT // ///////////////////////////////////////////////////////////////////////// -type JsonType struct { +// JSONType defined from schema: +// { +// "title": "Json Type", +// "type": "object", +// "properties": { +// "custom": { +// "title": "custom", +// "type": "string" +// }, +// "customId": { +// "title": "customId", +// "type": "integer" +// }, +// "items": { +// "title": "items", +// "type": "string" +// }, +// "system": { +// "title": "system", +// "type": "string" +// }, +// "type": { +// "title": "type", +// "type": "string" +// } +// } +// } +type JSONType struct { Custom string `json:"custom,omitempty" yaml:"custom,omitempty"` CustomID int `json:"customId,omitempty" yaml:"customId,omitempty"` Items string `json:"items,omitempty" yaml:"items,omitempty"` diff --git a/data/Operations.go b/data/Operations.go new file mode 100644 index 0000000..5516b65 --- /dev/null +++ b/data/Operations.go @@ -0,0 +1,21 @@ +package jiradata + +///////////////////////////////////////////////////////////////////////// +// This Code is Generated by SlipScheme Project: +// https://github.com/coryb/slipscheme +// +// Generated with command: +// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json +///////////////////////////////////////////////////////////////////////// +// DO NOT EDIT // +///////////////////////////////////////////////////////////////////////// + +// Operations defined from schema: +// { +// "title": "operations", +// "type": "array", +// "items": { +// "type": "string" +// } +// } +type Operations []string diff --git a/data/Status.go b/data/Status.go index a916318..0b2caaf 100644 --- a/data/Status.go +++ b/data/Status.go @@ -4,11 +4,69 @@ package jiradata // This Code is Generated by SlipScheme Project: // https://github.com/coryb/slipscheme // -// Generated with command: slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json +// Generated with command: +// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json ///////////////////////////////////////////////////////////////////////// // DO NOT EDIT // ///////////////////////////////////////////////////////////////////////// +// Status defined from schema: +// { +// "title": "Status", +// "type": "object", +// "properties": { +// "description": { +// "title": "description", +// "type": "string" +// }, +// "iconUrl": { +// "title": "iconUrl", +// "type": "string" +// }, +// "id": { +// "title": "id", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "self": { +// "title": "self", +// "type": "string" +// }, +// "statusCategory": { +// "title": "Status Category", +// "type": "object", +// "properties": { +// "colorName": { +// "title": "colorName", +// "type": "string" +// }, +// "id": { +// "title": "id", +// "type": "integer" +// }, +// "key": { +// "title": "key", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "self": { +// "title": "self", +// "type": "string" +// } +// } +// }, +// "statusColor": { +// "title": "statusColor", +// "type": "string" +// } +// } +// } type Status struct { Description string `json:"description,omitempty" yaml:"description,omitempty"` IconURL string `json:"iconUrl,omitempty" yaml:"iconUrl,omitempty"` diff --git a/data/StatusCategory.go b/data/StatusCategory.go index 889b3c1..fa6a736 100644 --- a/data/StatusCategory.go +++ b/data/StatusCategory.go @@ -4,11 +4,39 @@ package jiradata // This Code is Generated by SlipScheme Project: // https://github.com/coryb/slipscheme // -// Generated with command: slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json +// Generated with command: +// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json ///////////////////////////////////////////////////////////////////////// // DO NOT EDIT // ///////////////////////////////////////////////////////////////////////// +// StatusCategory defined from schema: +// { +// "title": "Status Category", +// "type": "object", +// "properties": { +// "colorName": { +// "title": "colorName", +// "type": "string" +// }, +// "id": { +// "title": "id", +// "type": "integer" +// }, +// "key": { +// "title": "key", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "self": { +// "title": "self", +// "type": "string" +// } +// } +// } type StatusCategory struct { ColorName string `json:"colorName,omitempty" yaml:"colorName,omitempty"` ID int `json:"id,omitempty" yaml:"id,omitempty"` diff --git a/data/Transition.go b/data/Transition.go index 34c0459..e84ae20 100644 --- a/data/Transition.go +++ b/data/Transition.go @@ -4,11 +4,161 @@ package jiradata // This Code is Generated by SlipScheme Project: // https://github.com/coryb/slipscheme // -// Generated with command: slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json +// Generated with command: +// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json ///////////////////////////////////////////////////////////////////////// // DO NOT EDIT // ///////////////////////////////////////////////////////////////////////// +// Transition defined from schema: +// { +// "title": "Transition", +// "type": "object", +// "properties": { +// "expand": { +// "title": "expand", +// "type": "string" +// }, +// "fields": { +// "title": "fields", +// "type": "object", +// "patternProperties": { +// ".+": { +// "title": "Field Meta", +// "type": "object", +// "properties": { +// "allowedValues": { +// "title": "allowedValues", +// "type": "array", +// "items": {} +// }, +// "autoCompleteUrl": { +// "title": "autoCompleteUrl", +// "type": "string" +// }, +// "hasDefaultValue": { +// "title": "hasDefaultValue", +// "type": "boolean" +// }, +// "key": { +// "title": "key", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "operations": { +// "title": "operations", +// "type": "array", +// "items": { +// "type": "string" +// } +// }, +// "required": { +// "title": "required", +// "type": "boolean" +// }, +// "schema": { +// "title": "Json Type", +// "type": "object", +// "properties": { +// "custom": { +// "title": "custom", +// "type": "string" +// }, +// "customId": { +// "title": "customId", +// "type": "integer" +// }, +// "items": { +// "title": "items", +// "type": "string" +// }, +// "system": { +// "title": "system", +// "type": "string" +// }, +// "type": { +// "title": "type", +// "type": "string" +// } +// } +// } +// } +// } +// } +// }, +// "hasScreen": { +// "title": "hasScreen", +// "type": "boolean" +// }, +// "id": { +// "title": "id", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "to": { +// "title": "Status", +// "type": "object", +// "properties": { +// "description": { +// "title": "description", +// "type": "string" +// }, +// "iconUrl": { +// "title": "iconUrl", +// "type": "string" +// }, +// "id": { +// "title": "id", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "self": { +// "title": "self", +// "type": "string" +// }, +// "statusCategory": { +// "title": "Status Category", +// "type": "object", +// "properties": { +// "colorName": { +// "title": "colorName", +// "type": "string" +// }, +// "id": { +// "title": "id", +// "type": "integer" +// }, +// "key": { +// "title": "key", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "self": { +// "title": "self", +// "type": "string" +// } +// } +// }, +// "statusColor": { +// "title": "statusColor", +// "type": "string" +// } +// } +// } +// } +// } type Transition struct { Expand string `json:"expand,omitempty" yaml:"expand,omitempty"` Fields FieldMetaMap `json:"fields,omitempty" yaml:"fields,omitempty"` diff --git a/data/Transitions.go b/data/Transitions.go index ecf1569..f88c4c5 100644 --- a/data/Transitions.go +++ b/data/Transitions.go @@ -4,9 +4,163 @@ package jiradata // This Code is Generated by SlipScheme Project: // https://github.com/coryb/slipscheme // -// Generated with command: slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json +// Generated with command: +// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json ///////////////////////////////////////////////////////////////////////// // DO NOT EDIT // ///////////////////////////////////////////////////////////////////////// +// Transitions defined from schema: +// { +// "title": "transitions", +// "type": "array", +// "items": { +// "title": "Transition", +// "type": "object", +// "properties": { +// "expand": { +// "title": "expand", +// "type": "string" +// }, +// "fields": { +// "title": "fields", +// "type": "object", +// "patternProperties": { +// ".+": { +// "title": "Field Meta", +// "type": "object", +// "properties": { +// "allowedValues": { +// "title": "allowedValues", +// "type": "array", +// "items": {} +// }, +// "autoCompleteUrl": { +// "title": "autoCompleteUrl", +// "type": "string" +// }, +// "hasDefaultValue": { +// "title": "hasDefaultValue", +// "type": "boolean" +// }, +// "key": { +// "title": "key", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "operations": { +// "title": "operations", +// "type": "array", +// "items": { +// "type": "string" +// } +// }, +// "required": { +// "title": "required", +// "type": "boolean" +// }, +// "schema": { +// "title": "Json Type", +// "type": "object", +// "properties": { +// "custom": { +// "title": "custom", +// "type": "string" +// }, +// "customId": { +// "title": "customId", +// "type": "integer" +// }, +// "items": { +// "title": "items", +// "type": "string" +// }, +// "system": { +// "title": "system", +// "type": "string" +// }, +// "type": { +// "title": "type", +// "type": "string" +// } +// } +// } +// } +// } +// } +// }, +// "hasScreen": { +// "title": "hasScreen", +// "type": "boolean" +// }, +// "id": { +// "title": "id", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "to": { +// "title": "Status", +// "type": "object", +// "properties": { +// "description": { +// "title": "description", +// "type": "string" +// }, +// "iconUrl": { +// "title": "iconUrl", +// "type": "string" +// }, +// "id": { +// "title": "id", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "self": { +// "title": "self", +// "type": "string" +// }, +// "statusCategory": { +// "title": "Status Category", +// "type": "object", +// "properties": { +// "colorName": { +// "title": "colorName", +// "type": "string" +// }, +// "id": { +// "title": "id", +// "type": "integer" +// }, +// "key": { +// "title": "key", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "self": { +// "title": "self", +// "type": "string" +// } +// } +// }, +// "statusColor": { +// "title": "statusColor", +// "type": "string" +// } +// } +// } +// } +// } +// } type Transitions []*Transition diff --git a/data/TransitionsFuncs.go b/data/TransitionsFuncs.go index 1735f53..336cf29 100644 --- a/data/TransitionsFuncs.go +++ b/data/TransitionsFuncs.go @@ -4,6 +4,9 @@ import ( "strings" ) +// Find will search the transitions for one that matches +// the given name. It will return a valid trantion that matches +// or nil func (t Transitions) Find(name string) *Transition { name = strings.ToLower(name) for _, trans := range t { diff --git a/data/TransitionsMeta.go b/data/TransitionsMeta.go index 2946ccb..80e8454 100644 --- a/data/TransitionsMeta.go +++ b/data/TransitionsMeta.go @@ -4,11 +4,176 @@ package jiradata // This Code is Generated by SlipScheme Project: // https://github.com/coryb/slipscheme // -// Generated with command: slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json +// Generated with command: +// slipscheme -pkg jiradata -overwrite ../schemas/TransitionsMeta.json ///////////////////////////////////////////////////////////////////////// // DO NOT EDIT // ///////////////////////////////////////////////////////////////////////// +// TransitionsMeta defined from schema: +// { +// "title": "Transitions Meta", +// "id": "https://docs.atlassian.com/jira/REST/schema/transitions-meta#", +// "type": "object", +// "properties": { +// "expand": { +// "title": "expand", +// "type": "string" +// }, +// "transitions": { +// "title": "transitions", +// "type": "array", +// "items": { +// "title": "Transition", +// "type": "object", +// "properties": { +// "expand": { +// "title": "expand", +// "type": "string" +// }, +// "fields": { +// "title": "fields", +// "type": "object", +// "patternProperties": { +// ".+": { +// "title": "Field Meta", +// "type": "object", +// "properties": { +// "allowedValues": { +// "title": "allowedValues", +// "type": "array", +// "items": {} +// }, +// "autoCompleteUrl": { +// "title": "autoCompleteUrl", +// "type": "string" +// }, +// "hasDefaultValue": { +// "title": "hasDefaultValue", +// "type": "boolean" +// }, +// "key": { +// "title": "key", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "operations": { +// "title": "operations", +// "type": "array", +// "items": { +// "type": "string" +// } +// }, +// "required": { +// "title": "required", +// "type": "boolean" +// }, +// "schema": { +// "title": "Json Type", +// "type": "object", +// "properties": { +// "custom": { +// "title": "custom", +// "type": "string" +// }, +// "customId": { +// "title": "customId", +// "type": "integer" +// }, +// "items": { +// "title": "items", +// "type": "string" +// }, +// "system": { +// "title": "system", +// "type": "string" +// }, +// "type": { +// "title": "type", +// "type": "string" +// } +// } +// } +// } +// } +// } +// }, +// "hasScreen": { +// "title": "hasScreen", +// "type": "boolean" +// }, +// "id": { +// "title": "id", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "to": { +// "title": "Status", +// "type": "object", +// "properties": { +// "description": { +// "title": "description", +// "type": "string" +// }, +// "iconUrl": { +// "title": "iconUrl", +// "type": "string" +// }, +// "id": { +// "title": "id", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "self": { +// "title": "self", +// "type": "string" +// }, +// "statusCategory": { +// "title": "Status Category", +// "type": "object", +// "properties": { +// "colorName": { +// "title": "colorName", +// "type": "string" +// }, +// "id": { +// "title": "id", +// "type": "integer" +// }, +// "key": { +// "title": "key", +// "type": "string" +// }, +// "name": { +// "title": "name", +// "type": "string" +// }, +// "self": { +// "title": "self", +// "type": "string" +// } +// } +// }, +// "statusColor": { +// "title": "statusColor", +// "type": "string" +// } +// } +// } +// } +// } +// } +// } +// } type TransitionsMeta struct { Expand string `json:"expand,omitempty" yaml:"expand,omitempty"` Transitions Transitions `json:"transitions,omitempty" yaml:"transitions,omitempty"` diff --git a/t/000setup.t b/t/000setup.t index 50e796b..038afb7 100755 --- a/t/000setup.t +++ b/t/000setup.t @@ -3,10 +3,10 @@ eval "$(curl -q -s https://raw.githubusercontent.com/coryb/osht/master/osht.sh)" cd $(dirname $0) jira="../jira --user admin" -PLAN 15 +PLAN 14 # clean out any old containers -RUNS sh -c "docker rm -f go-jira-test || true" +docker rm -f go-jira-test # start newt jira service RUNS docker run --detach --name go-jira-test --publish 8080:8080 go-jira-test:latest @@ -14,8 +14,8 @@ RUNS docker run --detach --name go-jira-test --publish 8080:8080 go-jira-test:la # wait a few seconds for it to bind to port 8080 RUNS sleep 10 -# wait for healthchecks to pass, curl will retry 60 times over 1 min waiting -RUNS curl -q -L --retry 60 --retry-delay 1 -f -s "http://localhost:8080/rest/api/2/serverInfo?doHealthCheck=1" +# wait for healthchecks to pass, curl will retry 60 times over 5 min waiting +RUNS curl -q -L --retry 360 --retry-delay 1 -f -s "http://localhost:8080/rest/api/2/serverInfo?doHealthCheck=1" # login to jira as admin user echo "admin123" | RUNS $jira login diff --git a/templates.go b/templates.go index 1b80ea5..44b64bc 100644 --- a/templates.go +++ b/templates.go @@ -1,37 +1,37 @@ package jira -var all_templates = map[string]string{ - "debug": default_debug_template, - "fields": default_debug_template, - "editmeta": default_debug_template, - "transmeta": default_debug_template, - "createmeta": default_debug_template, - "issuelinktypes": default_debug_template, - "list": default_list_template, - "table": default_table_template, - "view": default_view_template, - "edit": default_edit_template, - "transitions": default_transitions_template, - "components": default_components_template, - "issuetypes": default_issuetypes_template, - "create": default_create_template, - "comment": default_comment_template, - "transition": default_transition_template, - "request": default_debug_template, +var allTemplates = map[string]string{ + "debug": defaultDebugTemplate, + "fields": defaultDebugTemplate, + "editmeta": defaultDebugTemplate, + "transmeta": defaultDebugTemplate, + "createmeta": defaultDebugTemplate, + "issuelinktypes": defaultDebugTemplate, + "list": defaultListTemplate, + "table": defaultTableTemplate, + "view": defaultViewTemplate, + "edit": defaultEditTemplate, + "transitions": defaultTransitionsTemplate, + "components": defaultComponentsTemplate, + "issuetypes": defaultIssuetypesTemplate, + "create": defaultCreateTemplate, + "comment": defaultCommentTemplate, + "transition": defaultTransitionTemplate, + "request": defaultDebugTemplate, } -const default_debug_template = "{{ . | toJson}}\n" +const defaultDebugTemplate = "{{ . | toJson}}\n" -const default_list_template = "{{ range .issues }}{{ .key | append \":\" | printf \"%-12s\"}} {{ .fields.summary }}\n{{ end }}" +const defaultListTemplate = "{{ range .issues }}{{ .key | append \":\" | printf \"%-12s\"}} {{ .fields.summary }}\n{{ end }}" -const default_table_template = `+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+ +const defaultTableTemplate = `+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+ | {{ "Issue" | printf "%-14s" }} | {{ "Summary" | printf "%-55s" }} | {{ "Priority" | printf "%-12s" }} | {{ "Status" | printf "%-12s" }} | {{ "Age" | printf "%-10s" }} | {{ "Reporter" | printf "%-12s" }} | {{ "Assignee" | printf "%-12s" }} | +{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+ {{ range .issues }}| {{ .key | printf "%-14s"}} | {{ .fields.summary | abbrev 55 | printf "%-55s" }} | {{.fields.priority.name | printf "%-12s" }} | {{.fields.status.name | printf "%-12s" }} | {{.fields.created | age | printf "%-10s" }} | {{if .fields.reporter}}{{ .fields.reporter.name | printf "%-12s"}}{{else}}{{end}} | {{if .fields.assignee }}{{.fields.assignee.name | printf "%-12s" }}{{else}}{{end}} | {{ end }}+{{ "-" | rep 16 }}+{{ "-" | rep 57 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+{{ "-" | rep 12 }}+{{ "-" | rep 14 }}+{{ "-" | rep 14 }}+ ` -const default_view_template = `issue: {{ .key }}{{if .fields.created}} +const defaultViewTemplate = `issue: {{ .key }}{{if .fields.created}} created: {{ .fields.created | age }} ago{{end}}{{if .fields.status}} status: {{ .fields.status.name }}{{end}} summary: {{ .fields.summary }} @@ -54,7 +54,7 @@ comments: {{ or .body "" | indent 4}} {{end}}{{end}} ` -const default_edit_template = `# issue: {{ .key }} +const defaultEditTemplate = `# issue: {{ .key }} update: comment: - add: @@ -82,16 +82,16 @@ fields: # {{ or .body "" | indent 4 | comment}} # {{end}} ` -const default_transitions_template = `{{ range .transitions }}{{.id }}: {{.name}} +const defaultTransitionsTemplate = `{{ range .transitions }}{{.id }}: {{.name}} {{end}}` -const default_components_template = `{{ range . }}{{.id }}: {{.name}} +const defaultComponentsTemplate = `{{ range . }}{{.id }}: {{.name}} {{end}}` -const default_issuetypes_template = `{{ range .projects }}{{ range .issuetypes }}{{color "+bh"}}{{.name | append ":" | printf "%-13s" }}{{color "reset"}} {{.description}} +const defaultIssuetypesTemplate = `{{ range .projects }}{{ range .issuetypes }}{{color "+bh"}}{{.name | append ":" | printf "%-13s" }}{{color "reset"}} {{.description}} {{end}}{{end}}` -const default_create_template = `fields: +const defaultCreateTemplate = `fields: project: key: {{ or .overrides.project "" }} issuetype: @@ -112,11 +112,11 @@ const default_create_template = `fields: - name: {{.}}{{end}} - name:{{end}}` -const default_comment_template = `body: |~ +const defaultCommentTemplate = `body: |~ {{ or .overrides.comment "" | indent 2 }} ` -const default_transition_template = `update: +const defaultTransitionTemplate = `update: comment: - add: body: |~ diff --git a/util.go b/util.go index 5628b97..aa46eab 100644 --- a/util.go +++ b/util.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "encoding/json" - "errors" "fmt" "github.com/mgutz/ansi" "gopkg.in/coryb/yaml.v2" @@ -26,10 +25,12 @@ func homedir() string { return os.Getenv("HOME") } +// FindParentPaths will find all available paths from the current path up to the root +// that matches the given fileName path func FindParentPaths(fileName string) []string { cwd, _ := os.Getwd() - paths := make([]string, 0) + paths := []string{} // special case if homedir is not in current path then check there anyway homedir := homedir() @@ -57,12 +58,14 @@ func FindParentPaths(fileName string) []string { return paths } +// FindClosestParentPath finds the path that matches the given fileName path that is +// closest to the current working directory func FindClosestParentPath(fileName string) (string, error) { paths := FindParentPaths(fileName) if len(paths) > 0 { return paths[len(paths)-1], nil } - return "", errors.New(fmt.Sprintf("%s not found in parent directory hierarchy", fileName)) + return "", fmt.Errorf("%s not found in parent directory hierarchy", fileName) } func readFile(file string) string { @@ -92,34 +95,36 @@ func copyFile(src, dst string) (err error) { } func fuzzyAge(start string) (string, error) { - if t, err := time.Parse("2006-01-02T15:04:05.000-0700", start); err != nil { + t, err := time.Parse("2006-01-02T15:04:05.000-0700", start) + if err != nil { return "", err - } else { - delta := time.Now().Sub(t) - if delta.Minutes() < 2 { - return "a minute", nil - } else if dm := delta.Minutes(); dm < 45 { - return fmt.Sprintf("%d minutes", int(dm)), nil - } else if dm := delta.Minutes(); dm < 90 { - return "an hour", nil - } else if dh := delta.Hours(); dh < 24 { - return fmt.Sprintf("%d hours", int(dh)), nil - } else if dh := delta.Hours(); dh < 48 { - return "a day", nil - } else { - return fmt.Sprintf("%d days", int(delta.Hours()/24)), nil - } } + delta := time.Now().Sub(t) + if delta.Minutes() < 2 { + return "a minute", nil + } else if dm := delta.Minutes(); dm < 45 { + return fmt.Sprintf("%d minutes", int(dm)), nil + } else if dm := delta.Minutes(); dm < 90 { + return "an hour", nil + } else if dh := delta.Hours(); dh < 24 { + return fmt.Sprintf("%d hours", int(dh)), nil + } else if dh := delta.Hours(); dh < 48 { + return "a day", nil + } + return fmt.Sprintf("%d days", int(delta.Hours()/24)), nil } func dateFormat(format string, content string) (string, error) { - if t, err := time.Parse("2006-01-02T15:04:05.000-0700", content); err != nil { + t, err := time.Parse("2006-01-02T15:04:05.000-0700", content) + if err != nil { return "", err - } else { - return t.Format(format), nil } + return t.Format(format), nil } +// RunTemplate will run the give templateContent as a golang text/template +// and pass the provided data to the template execution. It will write +// the output to the provided "out" writer. func RunTemplate(templateContent string, data interface{}, out io.Writer) error { return runTemplate(templateContent, data, out) } @@ -131,11 +136,11 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error funcs := map[string]interface{}{ "toJson": func(content interface{}) (string, error) { - if bytes, err := json.MarshalIndent(content, "", " "); err != nil { + bytes, err := json.MarshalIndent(content, "", " ") + if err != nil { return "", err - } else { - return string(bytes), nil } + return string(bytes), nil }, "append": func(more string, content interface{}) (string, error) { switch value := content.(type) { @@ -144,13 +149,13 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error case []byte: return string(append(content.([]byte), []byte(more)...)), nil default: - return "", errors.New(fmt.Sprintf("Unknown type: %s", value)) + return "", fmt.Errorf("Unknown type: %s", value) } }, "indent": func(spaces int, content string) string { indent := make([]rune, spaces+1, spaces+1) indent[0] = '\n' - for i := 1; i < spaces+1; i += 1 { + for i := 1; i < spaces+1; i++ { indent[i] = ' ' } @@ -193,7 +198,7 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error }, "rep": func(count int, content string) string { var buffer bytes.Buffer - for i := 0; i < count; i += 1 { + for i := 0; i < count; i++ { buffer.WriteString(content) } return buffer.String() @@ -205,19 +210,19 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error return dateFormat(format, content) }, } - if tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent); err != nil { + tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent) + if err != nil { log.Errorf("Failed to parse template: %s", err) return err - } else { - if err := tmpl.Execute(out, data); err != nil { - log.Errorf("Failed to execute template: %s", err) - return err - } + } + if err := tmpl.Execute(out, data); err != nil { + log.Errorf("Failed to execute template: %s", err) + return err } return nil } -func responseToJson(resp *http.Response, err error) (interface{}, error) { +func responseToJSON(resp *http.Response, err error) (interface{}, error) { if err != nil { return nil, err } @@ -366,7 +371,7 @@ func mkdir(dir string) error { log.Errorf("Failed to stat %s: %s", dir, err) return err } else if err == nil && !stat.IsDir() { - err := fmt.Errorf("%s exists and is not a directory!", dir) + err := fmt.Errorf("%s exists and is not a directory", dir) log.Errorf("%s", err) return err } else {