grim/youtrack-import

Use the built in mime stuff to figure out the content type for attachments
package youtrack
import (
"bytes"
"encoding/xml"
"fmt"
"net/http"
"time"
)
type Issue struct {
Number int `yt:"numberInProject"`
Summary string `yt:"summary"`
Description string `yt:"description"`
Created time.Time `yt:"created"`
Updated time.Time `yt:"updated"`
UpdatedBy string `yt:"updaterName"`
Resolved time.Time `yt:"resolved"`
Reporter string `yt:"reporter"`
Assignee string
Voters []string `yt:"voterName"`
Watchers []string `yt:"watcherName"`
PermittedGroup string `yt:"permittedGroup"`
Markdown bool
Version string
Subsystem string
Priority string `yt:"priority"`
Type string `yt:"type"`
State string `yt:"state"`
Comments []Comment
Attachments []*Attachment
}
func (i *Issue) encode() xmlIssue {
x := xmlIssue{}
// required fields
x.AddField("numberInProject", fmt.Sprintf("%d", i.Number))
x.AddField("summary", i.Summary)
x.AddField("reporterName", i.Reporter)
x.AddField("Assignee", i.Assignee)
x.AddField("Type", i.Type)
x.AddField("Priority", i.Priority)
x.AddField("State", i.State)
// optional fields
if i.Markdown {
x.AddField("markdown", "true")
}
if !i.Created.IsZero() {
x.AddField("created", formatTimeString(i.Created))
}
if i.Description != "" {
x.AddField("description", i.Description)
}
if !i.Updated.IsZero() {
x.AddField("updated", formatTimeString(i.Updated))
}
if i.UpdatedBy != "" {
x.AddField("updaterName", i.UpdatedBy)
}
if !i.Resolved.IsZero() {
x.AddField("resolved", fmt.Sprintf("%d", i.Resolved.Unix()))
}
if len(i.Voters) > 0 {
x.AddFieldSlice("voterName", i.Voters)
}
if len(i.Watchers) > 0 {
x.AddFieldSlice("watcherName", i.Watchers)
}
if i.PermittedGroup != "" {
x.AddField("permittedGroup", i.PermittedGroup)
}
if i.Version != "" {
x.AddField("Fix Versions", i.Version)
}
if i.Subsystem != "" {
x.AddField("Subsystem", i.Subsystem)
}
sortComments(i.Comments)
for _, c := range i.Comments {
x.AddComment(c.Author, c.Text, c.Created, c.Updated, c.Markdown)
}
return x
}
func (c *Client) importIssuesRange(p *Project, s, e int) error {
issues := &xmlIssues{}
issues.Issues = make([]xmlIssue, e-s)
for i := s; i < e; i++ {
issues.Issues[i-s] = p.Issues[i].encode()
}
// convert the datastructure to an xml string
data, err := xml.Marshal(issues)
if err != nil {
return err
}
// create the reader for the put request
putBody := bytes.NewReader(data)
resp, err := c.xmlRequest(
http.MethodPut,
c.uri+"/import/"+p.ID+"/issues",
putBody,
)
if err != nil {
return err
}
defer resp.Body.Close()
return c.validateXmlImportReport(
[]int{http.StatusOK, http.StatusBadRequest},
resp,
)
}
func (c *Client) ImportIssues(p *Project) error {
r := 10
for s := 0; s < len(p.Issues); s += r {
e := s + r
if e > len(p.Issues) {
e = len(p.Issues)
}
fmt.Printf("importing issues %d-%d ... ", s, e-1)
if err := c.importIssuesRange(p, s, e); err != nil {
fmt.Printf("failed.\n")
return err
}
fmt.Printf("done.\n")
}
return nil
}