grim/youtrack-import

Fix the maxIssues bug where if you didn't specify a number of issues to import it didn't import any issues.
package bitbucket
import (
"fmt"
"regexp"
"strings"
"keep.imfreedom.org/grim/youtrack-import/youtrack"
)
var (
kindMap = map[string]string{
"bug": "Bug",
"enhancement": "Feature",
"proposal": "Feature",
"task": "Task",
}
statusMap = map[string]string{
"new": "Submitted",
"open": "Open",
"resolved": "Fixed",
"on hold": "Incomplete",
"invalid": "Can't Reproduce",
"duplicate": "Duplicate",
"wontfix": "Won't Fix",
"closed": "Fixed",
}
priorityMap = map[string]string{
"trivial": "Minor",
"minor": "Normal",
"major": "Major",
"critical": "Critical",
"blocker": "Show-stopper",
}
newlineRegex = regexp.MustCompile(`\r\n`)
)
func cleanupMessage(m map[string]*youtrack.User, nIssues int, projectID, repository, message string) string {
output := message
// replace \r\n with \n
output = newlineRegex.ReplaceAllString(output, "\n")
output = replaceKeywords(m, nIssues, projectID, repository, output)
output = replaceCreole(m, output)
return output
}
func (a *Archive) convertIssue(nIssues int, projectID, repository string, bb Issue, userMap map[string]*youtrack.User) *youtrack.Issue {
reporter := "admin"
if user, found := userMap[bb.Reporter.AccountID]; found {
reporter = user.Login
}
assignee := ""
if user, found := userMap[bb.Assignee.AccountID]; found {
assignee = user.Login
}
yt := &youtrack.Issue{
Number: bb.ID,
Summary: bb.Title,
Description: cleanupMessage(userMap, nIssues, projectID, repository, bb.Content),
Created: bb.CreatedOn,
Updated: bb.UpdatedOn,
Reporter: reporter,
Assignee: assignee,
Priority: bb.Priority,
Type: bb.Kind,
State: bb.Status,
Version: bb.Version,
Subsystem: bb.Component,
Markdown: true,
Comments: []*youtrack.Comment{},
}
yt.Voters = flattenAuthors(userMap, bb.Voters)
yt.Watchers = flattenAuthors(userMap, bb.Watchers)
// map field values from bitbucket to youtrack
if replace, found := priorityMap[yt.Priority]; found {
yt.Priority = replace
}
if replace, found := kindMap[yt.Type]; found {
yt.Type = replace
}
if replace, found := statusMap[yt.State]; found {
yt.State = replace
}
// add the comments and attachments
yt.Comments = a.convertComments(nIssues, projectID, repository, userMap, bb.ID)
yt.Attachments = a.convertAttachments(userMap, bb)
return yt
}
func (a *Archive) convertComments(nIssues int, projectID, repository string, userMap map[string]*youtrack.User, id int) []*youtrack.Comment {
comments := []*youtrack.Comment{}
for _, comment := range a.Comments {
if comment.Issue != id {
continue
}
// check if there were any stage changes with this comment
stateChanges := []string{}
for _, log := range a.Logs {
if log.Comment == comment.ID {
if log.Field == "content" {
stateChanges = append(stateChanges, "* **edited description**")
} else {
from := log.ChangedFrom
if from == "" {
from = "*unset*"
}
to := log.ChangedTo
if to == "" {
to = "*unset*"
}
change := fmt.Sprintf(
"* **%s**: %s ⟶ %s",
log.Field,
from,
to,
)
stateChanges = append(stateChanges, change)
}
}
}
if logContent := strings.Join(stateChanges, "\n"); logContent != "" {
comment.Content = logContent + "\n\n" + comment.Content
}
author := "admin"
if user, found := userMap[comment.User.AccountID]; found {
author = user.Login
}
ytComment := &youtrack.Comment{
Author: author,
Text: cleanupMessage(userMap, nIssues, projectID, repository, comment.Content),
Created: comment.CreatedOn,
Updated: comment.UpdatedOn,
Markdown: true,
}
comments = append(comments, ytComment)
}
return comments
}
func (a *Archive) convertAttachments(userMap map[string]*youtrack.User, issue Issue) []*youtrack.Attachment {
attachments := []*youtrack.Attachment{}
for _, attachment := range a.Attachments {
if attachment.Issue != issue.ID {
continue
}
author := "admin"
if user, found := userMap[attachment.User.AccountID]; found {
author = user.Login
}
reader, err := a.OpenAttachment(attachment.Path)
if err != nil {
fmt.Printf("%v\n", err)
continue
}
yt := youtrack.NewAttachment(attachment.Issue, author, issue.CreatedOn)
yt.AddFile(reader, attachment.Filename)
attachments = append(attachments, yt)
}
return attachments
}
func (a *Archive) convert(projectID, repository string, usersMap *UsersMap) (*youtrack.Project, error) {
users, err := a.ExtractUsers(usersMap)
if err != nil {
return nil, err
}
// create a slice of youtrack users for import
ytUsers := make([]*youtrack.User, len(users))
i := 0
for _, user := range users {
ytUsers[i] = user
i++
}
// create a string slice of all the components
components := make([]youtrack.OwnedField, len(a.Components))
for i := 0; i < len(a.Components); i++ {
components[i] = youtrack.OwnedField{Value: a.Components[i].Name}
}
// create a string slice of all the versions
versions := make([]youtrack.Version, len(a.Versions))
for i := 0; i < len(a.Versions); i++ {
versions[i] = youtrack.Version{Name: a.Versions[i].Name}
}
// convert all of the issues
nIssues := len(a.Issues)
issues := make([]*youtrack.Issue, nIssues)
for i := 0; i < nIssues; i++ {
issues[i] = a.convertIssue(nIssues, projectID, repository, a.Issues[i], users)
}
project := &youtrack.Project{
Users: ytUsers,
Subsystems: components,
Versions: versions,
Issues: issues,
}
return project, nil
}