func LoadComments(review DifferentialReview, readTransactions ReadTransactions, readTransactionComment ReadTransactionComment, lookupUser UserLookup) []comment.Comment { allTransactions, err := readTransactions(review.PHID) if err != nil { log.Fatal(err) } var comments []comment.Comment commentsByPHID := make(map[string]comment.Comment) rejectionCommentsByUser := make(map[string][]string) log.Printf("LOADCOMMENTS: Returning %d transactions", len(allTransactions)) for _, transaction := range allTransactions { author, err := lookupUser(transaction.AuthorPHID) if err != nil { log.Fatal(err) } c := comment.Comment{ Author: author.Email, Timestamp: fmt.Sprintf("%d", transaction.DateCreated), } if author.Email != "" { c.Author = author.Email } else { c.Author = author.UserName } if transaction.CommentPHID != nil { transactionComment, err := readTransactionComment(transaction.PHID) if err != nil { log.Fatal(err) } if transactionComment.FileName != "" { c.Location = &comment.Location{ Commit: transactionComment.Commit, Path: transactionComment.FileName, } if transactionComment.LineNumber != 0 { c.Location.Range = &comment.Range{ StartLine: transactionComment.LineNumber, } } } c.Description = transactionComment.Content if transactionComment.ReplyToCommentPHID != nil { // We assume that the parent has to have been processed before the child, // and enforce that by ordering the transactions in our queries. if replyTo, ok := commentsByPHID[*transactionComment.ReplyToCommentPHID]; ok { parentHash, err := replyTo.Hash() if err != nil { log.Fatal(err) } c.Parent = parentHash } } } // Set the resolved bit based on whether the change was approved or not. if transaction.Type == "differential:action" && transaction.NewValue != nil { action := *transaction.NewValue var resolved bool if action == "\"accept\"" { resolved = true c.Resolved = &resolved // Add child comments to all previous rejects by this user and make them accepts for _, rejectionCommentHash := range rejectionCommentsByUser[author.UserName] { approveComment := comment.Comment{ Author: c.Author, Timestamp: c.Timestamp, Resolved: &resolved, Parent: rejectionCommentHash, } comments = append(comments, approveComment) log.Printf("LOADCOMMENTS: Received approval. Adding child comment %v with parent hash %x", approveComment, rejectionCommentHash) } } else if action == "\"reject\"" { resolved = false c.Resolved = &resolved } } // Phabricator only publishes inline comments when you publish a top-level comment. // This results in a lot of empty top-level comments, which we do not want to mirror. // To work around this, we only return comments that are non-empty. if c.Parent != "" || c.Location != nil || c.Description != "" || c.Resolved != nil { comments = append(comments, c) commentsByPHID[transaction.PHID] = c //If this was a rejection comment, add it to ordered comment hash if c.Resolved != nil && *c.Resolved == false { commentHash, err := c.Hash() if err != nil { log.Fatal(err) } log.Printf("LOADCOMMENTS: Received rejection. Adding comment %v with hash %x", c, commentHash) rejectionCommentsByUser[author.UserName] = append(rejectionCommentsByUser[author.UserName], commentHash) } } } log.Printf("LOADCOMMENTS: Returning %d comments", len(comments)) return comments }