diff --git a/cmd/jkl/create.go b/cmd/jkl/create.go index 69c37ec..0306cb8 100644 --- a/cmd/jkl/create.go +++ b/cmd/jkl/create.go @@ -4,18 +4,19 @@ import ( "bytes" "errors" "flag" + "fmt" "io" "os" - "fmt" + "strings" "text/template" "otremblay.com/jkl" ) type CreateCmd struct { - args []string - project string - file string + args []string + project string + file string issuetype string } @@ -43,7 +44,7 @@ func (ccmd *CreateCmd) Create() error { } } - + if ccmd.project == "" { return ErrCcmdJiraProjectRequired } @@ -55,13 +56,22 @@ func (ccmd *CreateCmd) Create() error { if err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Error getting the CreateMeta for project [%s] and issue types [%s]", ccmd.project, isstype), err) } - + if !readfile { createTemplate.Execute(b, cm) } var iss *jkl.JiraIssue // TODO: Evil badbad don't do this. - em := &jkl.EditMeta{Fields: cm.Projects[0].IssueTypes[0].Fields} + var isst = cm.Projects[0].IssueTypes[0].Fields + for _, v := range cm.Projects[0].IssueTypes { + if strings.ToLower(isstype) == strings.ToLower(v.Name) { + isst = v.Fields + break + } + } + + em := &jkl.EditMeta{Fields: isst} + if ccmd.file != "" { iss, err = GetIssueFromFile(ccmd.file, b, em) if err != nil { diff --git a/cmd/jkl/editor.go b/cmd/jkl/editor.go index bb9fa31..9c59ea5 100644 --- a/cmd/jkl/editor.go +++ b/cmd/jkl/editor.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "io/ioutil" - "log" "os" "os/exec" "regexp" @@ -22,16 +21,21 @@ import ( // [System.get_env("EDITOR"), "nano", "vim", "vi"] // |> Enum.find(nil, fn (ed) -> System.find_executable(ed) != nil end) // end -var editors = []string{os.Getenv("EDITOR"), "nano", "vim", "vi"} +var editors = []string{"nano", "vim", "vi"} // GetEditor returns the path to an editor, taking $EDITOR in account func GetEditor() string { + if ed := os.Getenv("EDITOR"); ed != "" { + return ed + } + if ed := os.Getenv("VISUAL"); ed != "" { + return ed + } for _, ed := range editors { if p, err := exec.LookPath(ed); err == nil { return p } } - log.Fatal("No editor available; use flags.") return "" } @@ -105,7 +109,7 @@ func GetIssueFromFile(filename string, initial io.Reader, editMeta *jkl.EditMeta var spacex = regexp.MustCompile(`\s`) func IssueFromReader(f io.Reader, editMeta *jkl.EditMeta) *jkl.JiraIssue { - iss := &jkl.JiraIssue{Fields: &jkl.Fields{}} + iss := &jkl.JiraIssue{Fields: &jkl.Fields{ExtraFields: map[string]interface{}{}}} riss := reflect.ValueOf(iss).Elem() fieldsField := riss.FieldByName("Fields").Elem() currentField := reflect.Value{} @@ -157,6 +161,25 @@ func IssueFromReader(f io.Reader, editMeta *jkl.EditMeta) *jkl.JiraIssue { } } else if editMeta != nil { // If it's not valid, throw it at the createmeta. It will probably end up in ExtraFields. + val := strings.TrimSpace(strings.Join(parts[1:], ":")) + for fieldname, m := range editMeta.Fields { + var something interface{} = val + if strings.ToLower(m.Name) == strings.ToLower(potentialField) { + name := fieldname + for _, av := range m.AllowedValues { + if strings.ToLower(av.Name) == strings.ToLower(val) { + something = av + break + } + } + if m.Schema.CustomId > 0 { + name = fmt.Sprintf("custom_%d", m.Schema.CustomId) + } + iss.Fields.ExtraFields[name] = something + + break + } + } } if currentField.IsValid() { @@ -168,6 +191,8 @@ func IssueFromReader(f io.Reader, editMeta *jkl.EditMeta) *jkl.JiraIssue { currentField.SetString(newvalue) } } + + iss.EditMeta = editMeta return iss } diff --git a/cmd/jkl/task.go b/cmd/jkl/task.go index b9279a3..a24ccaa 100644 --- a/cmd/jkl/task.go +++ b/cmd/jkl/task.go @@ -19,11 +19,11 @@ func (t *TaskCmd) Handle() error { return t.Get(t.args[0]) } if c == 2 { - fmt.Println(t.args) + // fmt.Println(t.args) err := t.Transition(t.args[0], t.args[1]) if err != nil { - fmt.Println(err) - return t.Log(t.args[0], strings.Join(t.args[1:]," ")) + //fmt.Println(err) + return t.Log(t.args[0], strings.Join(t.args[1:], " ")) } } return ErrTaskSubCommandNotFound @@ -45,7 +45,7 @@ func (t *TaskCmd) Transition(taskKey, transition string) error { } func (t *TaskCmd) Log(taskKey, time string) error { -return jkl.LogWork(taskKey, time) + return jkl.LogWork(taskKey, time) } func (t *TaskCmd) Run() error { diff --git a/issue.go b/issue.go index 692cc38..ba51508 100644 --- a/issue.go +++ b/issue.go @@ -38,9 +38,10 @@ func (it *IssueType) RangeFieldSpecs() string { } type AllowedValue struct { - Id string - Self string - Value string + Id string `json:"id"` + Self string `json:"self"` + Value string `json:"value"` + Name string `json:"name"` } func (a *AllowedValue) String() string { @@ -252,6 +253,91 @@ type JiraIssue struct { var sprintRegexp = regexp.MustCompile(`name=([^,]+),`) +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() || isEmptyValue(reflect.Indirect(v)) + } + return false +} + +func (i *JiraIssue) MarshalJSON() ([]byte, error) { + fields := map[string]interface{}{} + vf := reflect.ValueOf(*(i.Fields)) + if i.EditMeta != nil && i.EditMeta.Fields != nil { + for k, f := range i.EditMeta.Fields { + name := k + if f.Schema.CustomId > 0 { + name = fmt.Sprintf("custom_%d", f.Schema.CustomId) + } + if val, ok := i.Fields.ExtraFields[name]; ok && val != nil { + if f.Schema.Type == "array" { + fields[name] = []interface{}{val} + continue + } + fields[name] = val + } else if val == nil { + delete(fields, name) + } + } + } + + for i := 0; i < vf.NumField(); i++ { + ft := vf.Type().Field(i) + fv := vf.Field(i) + + if ft.Name != "ExtraFields" && (fv.CanSet() || fv.CanInterface() || fv.CanAddr()) && fv.IsValid() { + name := strings.ToLower(ft.Name) + if alias, ok := ft.Tag.Lookup("json"); ok { + if alias != "" { + name = strings.Split(alias, ",")[0] + } + } + value := fv.Interface() + if value != nil { + fields[name] = value + + } else { + delete(fields, name) + } + } + } + fmt.Println(fields) + for k, v := range fields { + v2 := reflect.ValueOf(v) + for { + if v2.Kind() != reflect.Ptr && v2.Kind() != reflect.Interface { + break + } + v2 = v2.Elem() + } + if !v2.IsValid() || (v2.Kind() == reflect.Slice && v2.Len() == 0) { + delete(fields, k) + } + } + em := i.EditMeta + i.EditMeta = nil + defer func() { i.EditMeta = em }() + type Alias JiraIssue + return json.Marshal(&struct { + *Alias + Fields map[string]interface{} `json:"fields,omitempty"` + }{ + Fields: fields, + Alias: (*Alias)(i), + }) +} + func (i *JiraIssue) UnmarshalJSON(b []byte) error { tmp := map[string]json.RawMessage{} if len(b) == 0 { @@ -404,4 +490,4 @@ var issueTmplNoColorTxt = "{{.Key}}\t{{if .Fields.IssueType}}[{{.Fields.IssueTyp var CommentIdSeparator = "~" var issueTmpl = template.Must(template.New("issueTmpl").Parse(issueTmplTxt)) -var issueTmplNoColor = template.Must(template.New("issueTmplNoColor").Parse(issueTmplNoColorTxt)) \ No newline at end of file +var issueTmplNoColor = template.Must(template.New("issueTmplNoColor").Parse(issueTmplNoColorTxt)) diff --git a/jkl.go b/jkl.go index 2e85cc1..6d53e76 100644 --- a/jkl.go +++ b/jkl.go @@ -34,6 +34,7 @@ func bootHttpClient() { func Create(issue *JiraIssue) (*JiraIssue, error) { bootHttpClient() payload, err := formatPayload(issue) + if err != nil { return nil, err } @@ -218,7 +219,7 @@ func DoTransition(taskKey string, transitionName string) error { return err } var t *Transition - fmt.Println(iss.Transitions) + //fmt.Println(iss.Transitions) for _, transition := range iss.Transitions { if strings.ToLower(transition.Name) == strings.ToLower(transitionName) { t = transition @@ -381,7 +382,7 @@ func serializePayload(i interface{}) (io.Reader, error) { payload := bytes.NewBuffer(b) enc := json.NewEncoder(payload) err := enc.Encode(i) - fmt.Println(payload.String()) + //fmt.Println("payload: ", payload.String()) if err != nil { fmt.Println(err) return nil, err