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(conf core.Configuration) error {
 19	li.conf = conf
 20	return nil
 21}
 22
 23const keyLaunchpadID = "launchpad-id"
 24const keyLaunchpadLogin = "launchpad-login"
 25
 26func (li *launchpadImporter) ensurePerson(repo *cache.RepoCache, owner LPPerson) (*cache.IdentityCache, error) {
 27	// Look first in the cache
 28	i, err := repo.ResolveIdentityImmutableMetadata(keyLaunchpadLogin, owner.Login)
 29	if err == nil {
 30		return i, nil
 31	}
 32	if _, ok := err.(entity.ErrMultipleMatch); ok {
 33		return nil, err
 34	}
 35
 36	return repo.NewIdentityRaw(
 37		owner.Name,
 38		"",
 39		owner.Login,
 40		"",
 41		map[string]string{
 42			keyLaunchpadLogin: owner.Login,
 43		},
 44	)
 45}
 46
 47func (li *launchpadImporter) ImportAll(ctx context.Context, repo *cache.RepoCache, since time.Time) (<-chan core.ImportResult, error) {
 48	out := make(chan core.ImportResult)
 49	lpAPI := new(launchpadAPI)
 50
 51	err := lpAPI.Init()
 52	if err != nil {
 53		return nil, err
 54	}
 55
 56	lpBugs, err := lpAPI.SearchTasks(ctx, li.conf["project"])
 57	if err != nil {
 58		return nil, err
 59	}
 60
 61	go func() {
 62		for _, lpBug := range lpBugs {
 63			select {
 64			case <-ctx.Done():
 65				return
 66			default:
 67				lpBugID := fmt.Sprintf("%d", lpBug.ID)
 68				b, err := repo.ResolveBugCreateMetadata(keyLaunchpadID, lpBugID)
 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							keyLaunchpadID: lpBugID,
 90						},
 91					)
 92					if err != nil {
 93						out <- core.NewImportError(err, entity.Id(lpBugID))
 94						return
 95					}
 96
 97					out <- core.NewImportBug(b.Id())
 98
 99				}
100
101				/* Handle messages */
102				if len(lpBug.Messages) == 0 {
103					err := fmt.Sprintf("bug doesn't have any comments")
104					out <- core.NewImportNothing(entity.Id(lpBugID), err)
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(keyLaunchpadID, 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					op, err := b.AddCommentRaw(
134						owner,
135						createdAt.Unix(),
136						lpMessage.Content,
137						nil,
138						map[string]string{
139							keyLaunchpadID: lpMessage.ID,
140						})
141					if err != nil {
142						out <- core.NewImportError(err, op.Id())
143						return
144					}
145
146					out <- core.NewImportComment(op.Id())
147				}
148
149				err = b.CommitAsNeeded()
150				if err != nil {
151					out <- core.NewImportError(err, "")
152					return
153				}
154			}
155		}
156	}()
157
158	return out, nil
159}