import.go

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