import.go

  1package launchpad
  2
  3import (
  4	"context"
  5	"fmt"
  6	"time"
  7
  8	"github.com/git-bug/git-bug/bridge/core"
  9	"github.com/git-bug/git-bug/cache"
 10	"github.com/git-bug/git-bug/entity"
 11	"github.com/git-bug/git-bug/util/text"
 12)
 13
 14type launchpadImporter struct {
 15	conf core.Configuration
 16}
 17
 18func (li *launchpadImporter) Init(_ context.Context, repo *cache.RepoCache, conf core.Configuration) error {
 19	li.conf = conf
 20	return nil
 21}
 22
 23func (li *launchpadImporter) ensurePerson(repo *cache.RepoCache, owner LPPerson) (*cache.IdentityCache, error) {
 24	// Look first in the cache
 25	i, err := repo.Identities().ResolveIdentityImmutableMetadata(metaKeyLaunchpadLogin, owner.Login)
 26	if err == nil {
 27		return i, nil
 28	}
 29	if entity.IsErrMultipleMatch(err) {
 30		return nil, err
 31	}
 32
 33	return repo.Identities().NewRaw(
 34		owner.Name,
 35		"",
 36		owner.Login,
 37		"",
 38		nil,
 39		map[string]string{
 40			metaKeyLaunchpadLogin: owner.Login,
 41		},
 42	)
 43}
 44
 45func (li *launchpadImporter) ImportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ImportResult, error) {
 46	out := make(chan core.ImportResult)
 47	lpAPI := new(launchpadAPI)
 48
 49	err := lpAPI.Init()
 50	if err != nil {
 51		return nil, err
 52	}
 53
 54	lpBugs, err := lpAPI.SearchTasks(ctx, li.conf["project"])
 55	if err != nil {
 56		return nil, err
 57	}
 58
 59	go func() {
 60		for _, lpBug := range lpBugs {
 61			select {
 62			case <-ctx.Done():
 63				return
 64			default:
 65				lpBugID := fmt.Sprintf("%d", lpBug.ID)
 66				b, err := repo.Bugs().ResolveMatcher(func(excerpt *cache.BugExcerpt) bool {
 67					return excerpt.CreateMetadata[core.MetaKeyOrigin] == target &&
 68						excerpt.CreateMetadata[metaKeyLaunchpadID] == lpBugID
 69				})
 70				if err != nil && !entity.IsErrNotFound(err) {
 71					out <- core.NewImportError(err, entity.Id(lpBugID))
 72					return
 73				}
 74
 75				owner, err := li.ensurePerson(repo, lpBug.Owner)
 76				if err != nil {
 77					out <- core.NewImportError(err, entity.Id(lpBugID))
 78					return
 79				}
 80
 81				if entity.IsErrNotFound(err) {
 82					createdAt, _ := time.Parse(time.RFC3339, lpBug.CreatedAt)
 83					b, _, err = repo.Bugs().NewRaw(
 84						owner,
 85						createdAt.Unix(),
 86						text.CleanupOneLine(lpBug.Title),
 87						text.Cleanup(lpBug.Description),
 88						nil,
 89						map[string]string{
 90							core.MetaKeyOrigin: target,
 91							metaKeyLaunchpadID: lpBugID,
 92						},
 93					)
 94					if err != nil {
 95						out <- core.NewImportError(err, entity.Id(lpBugID))
 96						return
 97					}
 98
 99					out <- core.NewImportBug(b.Id())
100
101				}
102
103				/* Handle messages */
104				if len(lpBug.Messages) == 0 {
105					return
106				}
107
108				// The Launchpad API returns the bug description as the first
109				// comment, so skip it.
110				for _, lpMessage := range lpBug.Messages[1:] {
111					_, err := b.ResolveOperationWithMetadata(metaKeyLaunchpadID, lpMessage.ID)
112					if err != nil && err != cache.ErrNoMatchingOp {
113						out <- core.NewImportError(err, entity.Id(lpMessage.ID))
114						return
115					}
116
117					// If this comment already exists, we are probably
118					// updating an existing bug. We do not want to duplicate
119					// the comments, so let us just skip this one.
120					// TODO: Can Launchpad comments be edited?
121					if err == nil {
122						continue
123					}
124
125					owner, err := li.ensurePerson(repo, lpMessage.Owner)
126					if err != nil {
127						out <- core.NewImportError(err, "")
128						return
129					}
130
131					// This is a new comment, we can add it.
132					createdAt, _ := time.Parse(time.RFC3339, lpMessage.CreatedAt)
133					commentId, _, err := b.AddCommentRaw(
134						owner,
135						createdAt.Unix(),
136						text.Cleanup(lpMessage.Content),
137						nil,
138						map[string]string{
139							metaKeyLaunchpadID: lpMessage.ID,
140						})
141					if err != nil {
142						out <- core.NewImportError(err, b.Id())
143						return
144					}
145
146					out <- core.NewImportComment(b.Id(), commentId)
147				}
148
149				if !b.NeedCommit() {
150					out <- core.NewImportNothing(b.Id(), "no imported operation")
151				} else if err := b.Commit(); err != nil {
152					out <- core.NewImportError(err, "")
153					return
154				}
155			}
156		}
157	}()
158
159	return out, nil
160}