// 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 } }