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