drone/app/api/controller/repo/list_branches.go

225 lines
6.4 KiB
Go

// Copyright 2023 Harness, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package repo
import (
"context"
"fmt"
"github.com/harness/gitness/app/api/controller"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/app/services/protection"
"github.com/harness/gitness/git"
"github.com/harness/gitness/git/sha"
"github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/gotidy/ptr"
)
// ListBranches lists the branches of a repo.
func (c *Controller) ListBranches(ctx context.Context,
session *auth.Session,
repoRef string,
filter *types.BranchFilter,
) ([]types.BranchExtended, error) {
repo, err := c.getRepoCheckAccess(ctx, session, repoRef, enum.PermissionRepoView)
if err != nil {
return nil, err
}
rpcOut, err := c.git.ListBranches(ctx, &git.ListBranchesParams{
ReadParams: git.CreateReadParams(repo),
IncludeCommit: filter.IncludeCommit,
Query: filter.Query,
Sort: mapToRPCBranchSortOption(filter.Sort),
Order: mapToRPCSortOrder(filter.Order),
Page: int32(filter.Page),
PageSize: int32(filter.Size),
})
if err != nil {
return nil, fmt.Errorf("fail to get the list of branches from git: %w", err)
}
branches := rpcOut.Branches
metadata, err := c.collectBranchMetadata(ctx, repo, branches, filter.BranchMetadataOptions)
if err != nil {
return nil, fmt.Errorf("fail to collect branch metadata: %w", err)
}
response := make([]types.BranchExtended, len(branches))
for i := range branches {
response[i].Branch, err = controller.MapBranch(branches[i])
if err != nil {
return nil, fmt.Errorf("failed to map branch: %w", err)
}
response[i].IsDefault = repo.DefaultBranch == branches[i].Name
metadata.apply(i, &response[i])
}
return response, nil
}
// collectBranchMetadata collects the metadata for the provided list of branches.
// The metadata includes check, rules, pull requests, and branch divergences.
// Each of these would be returned only if the corresponding option is true.
func (c *Controller) collectBranchMetadata(
ctx context.Context,
repo *types.Repository,
branches []git.Branch,
options types.BranchMetadataOptions,
) (branchMetadataOutput, error) {
var (
checkSummary map[sha.SHA]types.CheckCountSummary
branchRuleMap map[string][]types.RuleInfo
pullReqMap map[string][]*types.PullReq
divergences *git.GetCommitDivergencesOutput
err error
)
if options.IncludeChecks {
commitSHAs := make([]string, len(branches))
for i := range branches {
commitSHAs[i] = branches[i].SHA.String()
}
checkSummary, err = c.checkStore.ResultSummary(ctx, repo.ID, commitSHAs)
if err != nil {
return branchMetadataOutput{}, fmt.Errorf("fail to fetch check summary for commits: %w", err)
}
}
if options.IncludeRules {
rules, err := c.protectionManager.ForRepository(ctx, repo.ID)
if err != nil {
return branchMetadataOutput{}, fmt.Errorf("failed to fetch protection rules for the repository: %w", err)
}
branchRuleMap = make(map[string][]types.RuleInfo)
for i := range branches {
branchName := branches[i].Name
branchRuleInfos, err := protection.GetRuleInfos(
rules,
repo.DefaultBranch,
branchName,
protection.RuleInfoFilterStatusActive,
protection.RuleInfoFilterTypeBranch)
if err != nil {
return branchMetadataOutput{}, fmt.Errorf("failed get branch rule infos: %w", err)
}
branchRuleMap[branchName] = branchRuleInfos
}
}
if options.IncludePullReqs {
branchNames := make([]string, len(branches))
for i := range branches {
branchNames[i] = branches[i].Name
}
pullReqMap, err = c.pullReqStore.ListOpenByBranchName(ctx, repo.ID, branchNames)
if err != nil {
return branchMetadataOutput{}, fmt.Errorf("fail to fetch pull requests per branch: %w", err)
}
}
if options.MaxDivergence > 0 {
readParams := git.CreateReadParams(repo)
divergenceRequests := make([]git.CommitDivergenceRequest, len(branches))
for i := range branches {
divergenceRequests[i].From = branches[i].Name
divergenceRequests[i].To = repo.DefaultBranch
}
divergences, err = c.git.GetCommitDivergences(ctx, &git.GetCommitDivergencesParams{
ReadParams: readParams,
MaxCount: int32(options.MaxDivergence),
Requests: divergenceRequests,
})
if err != nil {
return branchMetadataOutput{}, fmt.Errorf("fail to fetch commit divergences: %w", err)
}
}
return branchMetadataOutput{
checkSummary: checkSummary,
branchRuleMap: branchRuleMap,
pullReqMap: pullReqMap,
divergences: divergences,
}, nil
}
type branchMetadataOutput struct {
checkSummary map[sha.SHA]types.CheckCountSummary
branchRuleMap map[string][]types.RuleInfo
pullReqMap map[string][]*types.PullReq
divergences *git.GetCommitDivergencesOutput
}
func (metadata branchMetadataOutput) apply(
idx int,
branch *types.BranchExtended,
) {
if metadata.checkSummary != nil {
branch.CheckSummary = ptr.Of(metadata.checkSummary[branch.SHA])
}
if metadata.branchRuleMap != nil {
branch.Rules = metadata.branchRuleMap[branch.Name]
}
if metadata.pullReqMap != nil {
branch.PullRequests = metadata.pullReqMap[branch.Name]
}
if metadata.divergences != nil {
branch.CommitDivergence = ptr.Of(types.CommitDivergence(metadata.divergences.Divergences[idx]))
}
}
func mapToRPCBranchSortOption(o enum.BranchSortOption) git.BranchSortOption {
switch o {
case enum.BranchSortOptionDate:
return git.BranchSortOptionDate
case enum.BranchSortOptionName:
return git.BranchSortOptionName
case enum.BranchSortOptionDefault:
return git.BranchSortOptionDefault
default:
// no need to error out - just use default for sorting
return git.BranchSortOptionDefault
}
}
func mapToRPCSortOrder(o enum.Order) git.SortOrder {
switch o {
case enum.OrderAsc:
return git.SortOrderAsc
case enum.OrderDesc:
return git.SortOrderDesc
case enum.OrderDefault:
return git.SortOrderDefault
default:
// no need to error out - just use default for sorting
return git.SortOrderDefault
}
}