From 49cc971dbab1466bf390786ea35391f46e1dce7e Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Fri, 21 Dec 2018 20:01:02 +0100 Subject: Launchpad bridge: fetch comments. --- bridge/launchpad/import.go | 46 ++++++++++++++++++++++++++++-- bridge/launchpad/launchpad_api.go | 59 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/bridge/launchpad/import.go b/bridge/launchpad/import.go index 074b4bf3..10d25e6c 100644 --- a/bridge/launchpad/import.go +++ b/bridge/launchpad/import.go @@ -44,15 +44,18 @@ func (li *launchpadImporter) ImportAll(repo *cache.RepoCache) error { } for _, lpBug := range lpBugs { + var b *cache.BugCache + var err error + lpBugID := fmt.Sprintf("%d", lpBug.ID) - _, err := repo.ResolveBugCreateMetadata(keyLaunchpadID, lpBugID) + b, err = repo.ResolveBugCreateMetadata(keyLaunchpadID, lpBugID) if err != nil && err != bug.ErrBugNotExist { return err } if err == bug.ErrBugNotExist { createdAt, _ := time.Parse(time.RFC3339, lpBug.CreatedAt) - _, err := repo.NewBugRaw( + b, err = repo.NewBugRaw( li.makePerson(lpBug.Owner), createdAt.Unix(), lpBug.Title, @@ -70,6 +73,45 @@ func (li *launchpadImporter) ImportAll(repo *cache.RepoCache) error { fmt.Println("TODO: Update bug") } + /* Handle messages */ + if len(lpBug.Messages) == 0 { + return errors.Wrapf(err, "failed to fetch comments for bug #%s", lpBugID) + } + + // The Launchpad API returns the bug description as the first + // comment, so skip it. + for _, lpMessage := range lpBug.Messages[1:] { + _, err := b.ResolveTargetWithMetadata(keyLaunchpadID, lpMessage.ID) + if err != nil && err != cache.ErrNoMatchingOp { + return errors.Wrapf(err, "failed to fetch comments for bug #%s", lpBugID) + } + + // If this comment already exists, we are probably + // updating an existing bug. We do not want to duplicate + // the comments, so let us just skip this one. + // TODO: Can Launchpad comments be edited? + if err == nil { + continue + } + + // This is a new comment, we can add it. + createdAt, _ := time.Parse(time.RFC3339, lpMessage.CreatedAt) + err = b.AddCommentRaw( + li.makePerson(lpMessage.Owner), + createdAt.Unix(), + lpMessage.Content, + nil, + map[string]string{ + keyLaunchpadID: lpMessage.ID, + }) + if err != nil { + return errors.Wrapf(err, "failed to add comment to bug #%s", lpBugID) + } + } + err = b.CommitAsNeeded() + if err != nil { + return err + } } return nil } diff --git a/bridge/launchpad/launchpad_api.go b/bridge/launchpad/launchpad_api.go index 849ef925..8cafa241 100644 --- a/bridge/launchpad/launchpad_api.go +++ b/bridge/launchpad/launchpad_api.go @@ -5,7 +5,6 @@ package launchpad * https://launchpad.net/+apidoc/devel.html * * TODO: - * - Retrieve all messages associated to bugs * - Retrieve bug status * - Retrieve activity log * - SearchTasks should yield bugs one by one @@ -78,6 +77,15 @@ type LPBug struct { Owner LPPerson `json:"owner_link"` Description string `json:"description"` CreatedAt string `json:"date_created"` + Messages []LPMessage +} + +// LPMessage describes a comment on a bug report +type LPMessage struct { + Content string `json:"content"` + CreatedAt string `json:"date_created"` + Owner LPPerson `json:"owner_link"` + ID string `json:"self_link"` } type launchpadBugEntry struct { @@ -91,6 +99,11 @@ type launchpadAnswer struct { NextLink string `json:"next_collection_link"` } +type launchpadMessageAnswer struct { + Entries []LPMessage `json:"entries"` + NextLink string `json:"next_collection_link"` +} + type launchpadAPI struct { client *http.Client } @@ -113,6 +126,7 @@ func (lapi *launchpadAPI) SearchTasks(project string) ([]LPBug, error) { } queryParams := url.Values{} queryParams.Add("ws.op", "searchTasks") + queryParams.Add("order_by", "-date_last_updated") for _, validStatus := range validStatuses { queryParams.Add("status", validStatus) } @@ -175,5 +189,48 @@ func (lapi *launchpadAPI) queryBug(url string) (LPBug, error) { return bug, err } + /* Fetch messages */ + messagesCollectionLink := fmt.Sprintf("%s/bugs/%d/messages", apiRoot, bug.ID) + messages, err := lapi.queryMessages(messagesCollectionLink) + if err != nil { + return bug, err + } + bug.Messages = messages + return bug, nil } + +func (lapi *launchpadAPI) queryMessages(messagesURL string) ([]LPMessage, error) { + var messages []LPMessage + + for { + req, err := http.NewRequest("GET", messagesURL, nil) + if err != nil { + return nil, err + } + + resp, err := lapi.client.Do(req) + if err != nil { + return nil, err + } + + var result launchpadMessageAnswer + + err = json.NewDecoder(resp.Body).Decode(&result) + _ = resp.Body.Close() + + if err != nil { + return nil, err + } + + messages = append(messages, result.Entries...) + + // Launchpad only returns 75 results at a time. We get the next + // page and run another query, unless there is no other page. + messagesURL = result.NextLink + if messagesURL == "" { + break + } + } + return messages, nil +} -- cgit