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