152 lines
3.9 KiB
Go
152 lines
3.9 KiB
Go
package github
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
)
|
|
|
|
// buildReviewSearchQuery assembles the full GitHub search query.
|
|
// When customQuery is non-empty, it is used verbatim as the entire query.
|
|
// Otherwise a query is built from scope + optional filter qualifier.
|
|
func buildReviewSearchQuery(scope, filter, customQuery string) string {
|
|
if customQuery != "" {
|
|
return customQuery
|
|
}
|
|
var base string
|
|
if scope == ReviewScopeUser {
|
|
base = "type:pr state:open user-review-requested:@me"
|
|
} else {
|
|
base = "type:pr state:open review-requested:@me"
|
|
}
|
|
if filter != "" {
|
|
base += " " + filter
|
|
}
|
|
return base
|
|
}
|
|
|
|
// getPRFeedback fetches aggregated feedback for a PR using any Client implementation.
|
|
// This shared function eliminates duplication between GHClient and PATClient.
|
|
func getPRFeedback(ctx context.Context, c Client, owner, repo string, number int) (*PRFeedback, error) {
|
|
pr, err := c.GetPR(ctx, owner, repo, number)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
reviews, err := c.ListPRReviews(ctx, owner, repo, number)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
comments, err := c.ListPRComments(ctx, owner, repo, number, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
checks, err := c.ListCheckRuns(ctx, owner, repo, pr.HeadSHA)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hasIssues := hasFailingChecks(checks) || hasChangesRequested(reviews)
|
|
return &PRFeedback{
|
|
PR: pr,
|
|
Reviews: reviews,
|
|
Comments: comments,
|
|
Checks: checks,
|
|
HasIssues: hasIssues,
|
|
}, nil
|
|
}
|
|
|
|
// convertRawCheckRuns converts raw ghCheckRun structs into the domain CheckRun type.
|
|
func convertRawCheckRuns(raw []ghCheckRun) []CheckRun {
|
|
checks := make([]CheckRun, len(raw))
|
|
for i, cr := range raw {
|
|
conclusion := ""
|
|
if cr.Conclusion != nil {
|
|
conclusion = *cr.Conclusion
|
|
}
|
|
output := ""
|
|
if cr.Output.Summary != nil {
|
|
output = *cr.Output.Summary
|
|
}
|
|
checks[i] = CheckRun{
|
|
Name: cr.Name,
|
|
Status: cr.Status,
|
|
Conclusion: conclusion,
|
|
HTMLURL: cr.HTMLURL,
|
|
Output: output,
|
|
StartedAt: parseTimePtr(cr.StartedAt),
|
|
CompletedAt: parseTimePtr(cr.CompletedAt),
|
|
}
|
|
}
|
|
return checks
|
|
}
|
|
|
|
// convertRawComments converts raw ghComment structs into the domain PRComment type.
|
|
func convertRawComments(raw []ghComment) []PRComment {
|
|
comments := make([]PRComment, len(raw))
|
|
for i, c := range raw {
|
|
comments[i] = PRComment{
|
|
ID: c.ID,
|
|
Author: c.User.Login,
|
|
AuthorAvatar: c.User.AvatarURL,
|
|
Body: c.Body,
|
|
Path: c.Path,
|
|
Line: c.Line,
|
|
Side: c.Side,
|
|
CreatedAt: c.CreatedAt,
|
|
UpdatedAt: c.UpdatedAt,
|
|
InReplyTo: c.InReplyTo,
|
|
}
|
|
}
|
|
return comments
|
|
}
|
|
|
|
// convertSearchItemToPR converts common search result fields into a PR struct.
|
|
func convertSearchItemToPR(
|
|
number int, title, htmlURL, state, authorLogin, repositoryURL string,
|
|
draft bool, createdAt, updatedAt time.Time,
|
|
) *PR {
|
|
owner, repo := parseRepoURL(repositoryURL)
|
|
return &PR{
|
|
Number: number,
|
|
Title: title,
|
|
HTMLURL: htmlURL,
|
|
State: state,
|
|
AuthorLogin: authorLogin,
|
|
Draft: draft,
|
|
RepoOwner: owner,
|
|
RepoName: repo,
|
|
CreatedAt: createdAt,
|
|
UpdatedAt: updatedAt,
|
|
}
|
|
}
|
|
|
|
// latestReviewByAuthor returns a map of the most recent review per author.
|
|
func latestReviewByAuthor(reviews []PRReview) map[string]PRReview {
|
|
latest := make(map[string]PRReview)
|
|
for _, r := range reviews {
|
|
existing, ok := latest[r.Author]
|
|
if !ok || r.CreatedAt.After(existing.CreatedAt) {
|
|
latest[r.Author] = r
|
|
}
|
|
}
|
|
return latest
|
|
}
|
|
|
|
// hasFailingChecks returns true if any completed check run has failed.
|
|
func hasFailingChecks(checks []CheckRun) bool {
|
|
for _, c := range checks {
|
|
if c.Status == checkStatusCompleted && c.Conclusion == checkConclusionFail {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// hasChangesRequested returns true if any review has requested changes.
|
|
func hasChangesRequested(reviews []PRReview) bool {
|
|
for _, r := range reviews {
|
|
if r.State == reviewStateChangesRequested {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|