scorecard/clients/gitlabrepo/graphql.go
jimrobison fa42daff71
🐛 Gitlab status updates (#3052)
* doc: Updating gitlab support validation status

Signed-off-by: Robison, Jim B <jim.b.robison@lmco.com>

* bug: Updated  logic for gitlab to prevent exceptions based on releases

Signed-off-by: Robison, Jim B <jim.b.robison@lmco.com>

* test: Added initial tests for gitlab branches

Signed-off-by: Robison, Jim B <jim.b.robison@lmco.com>

* doc: Updated general README

Signed-off-by: Robison, Jim B <jim.b.robison@lmco.com>

* refactor: Cleaned up the query for pipelines to be focused on the commitID

Signed-off-by: Robison, Jim B <jim.b.robison@lmco.com>

* feat: Allowed for a non-graphql method of retrieving MRs associated to a commit

Signed-off-by: Robison, Jim B <jim.b.robison@lmco.com>

* doc: Updated status for the CI-Tests

Signed-off-by: Robison, Jim B <jim.b.robison@lmco.com>

* bug: Updated the host url for graphql querying. This enabled the removal of the code added for handling empty returns when executing against a non-gitlab.com repository.

Signed-off-by: Robison, Jim B <jim.b.robison@lmco.com>

---------

Signed-off-by: Robison, Jim B <jim.b.robison@lmco.com>
Co-authored-by: Raghav Kaul <8695110+raghavkaul@users.noreply.github.com>
2023-05-26 16:45:46 +00:00

144 lines
3.8 KiB
Go

// Copyright 2023 OpenSSF Scorecard Authors
//
// 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 gitlabrepo
import (
"context"
"errors"
"fmt"
"net/http"
"os"
"regexp"
"strconv"
"time"
"github.com/shurcooL/graphql"
"golang.org/x/oauth2"
)
//nolint:govet
type graphqlHandler struct {
err error
client *http.Client
graphClient *graphql.Client
ctx context.Context
repourl *repoURL
}
func (handler *graphqlHandler) init(ctx context.Context, repourl *repoURL) {
handler.ctx = ctx
handler.repourl = repourl
handler.err = nil
src := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: os.Getenv("GITLAB_AUTH_TOKEN")},
)
handler.client = oauth2.NewClient(context.Background(), src)
handler.graphClient = graphql.NewClient(fmt.Sprintf("%s/api/graphql", repourl.Host()), handler.client)
}
//nolint:govet
type graphqlData struct {
Project struct {
MergeRequests struct {
Nodes []graphqlMergeRequestNode `graphql:"nodes"`
} `graphql:"mergeRequests(sort: MERGED_AT_DESC, state: merged, mergedBefore: $mergedBefore)"`
} `graphql:"project(fullPath: $fullPath)"`
QueryComplexity struct {
Limit int `graphql:"limit"`
Score int `graphql:"score"`
} `graphql:"queryComplexity"`
}
//nolint:govet
type graphqlMergeRequestNode struct {
ID GitlabGID `graphql:"id"`
IID string `graphql:"iid"`
MergedAt time.Time `graphql:"mergedAt"`
Author struct {
Username string `graphql:"username"`
ID GitlabGID `graphql:"id"`
} `graphql:"author"`
MergedBy struct {
Username string `graphql:"username"`
ID GitlabGID `graphql:"id"`
} `graphql:"mergeUser"`
Commits struct {
Nodes []struct {
SHA string `graphql:"sha"`
} `graphql:"nodes"`
} `graphql:"commits"`
Reviewers struct {
Nodes []struct {
Username string `graphql:"username"`
ID GitlabGID `graphql:"id"`
MergeRequestInteraction struct {
ReviewState string `graphql:"reviewState"`
} `graphql:"mergeRequestInteraction"`
} `graphql:"nodes"`
} `graphql:"reviewers"`
Approvers struct {
Nodes []struct {
Username string `graphql:"username"`
ID GitlabGID `graphql:"id"`
} `graphql:"nodes"`
} `graphql:"approvedBy"`
MergeCommitSHA string `graphql:"mergeCommitSha"`
// Labels struct {
// Nodes []struct {
// Title string `graphql:"title"`
// } `graphql:"nodes"`
// } `graphql:"labels"`
}
type GitlabGID struct {
Type string
ID int
}
var errGitlabID = errors.New("failed to parse gitlab id")
func (g *GitlabGID) UnmarshalJSON(data []byte) error {
re := regexp.MustCompile(`gid:\/\/gitlab\/(\w+)\/(\d+)`)
m := re.FindStringSubmatch(string(data))
if len(m) < 3 {
return fmt.Errorf("%w: %s", errGitlabID, string(data))
}
g.Type = m[1]
id, err := strconv.Atoi(m[2])
if err != nil {
return fmt.Errorf("gid parse error: %w", err)
}
g.ID = id
return nil
}
func (handler *graphqlHandler) getMergeRequestsDetail(before *time.Time) (graphqlData, error) {
data := graphqlData{}
path := fmt.Sprintf("%s/%s", handler.repourl.owner, handler.repourl.project)
params := map[string]interface{}{
"fullPath": path,
"mergedBefore": before,
}
err := handler.graphClient.Query(context.Background(), &data, params)
if err != nil {
return graphqlData{}, fmt.Errorf("couldn't query gitlab graphql for merge requests: %w", err)
}
return data, nil
}