Skip to content

Commit

Permalink
New Get Repository Environment Info API (#64)
Browse files Browse the repository at this point in the history
  • Loading branch information
yahavi committed Jan 26, 2023
1 parent 8e82000 commit d41c3fc
Show file tree
Hide file tree
Showing 16 changed files with 231 additions and 12 deletions.
31 changes: 25 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Froggit-Go

[![Frogbot](images/header.png)](#readme)

</div>

Froggit-Go is a Go library, allowing to perform actions on VCS providers.
Expand Down Expand Up @@ -41,6 +42,7 @@ Currently supported providers are: [GitHub](#github), [Bitbucket Server](#bitbuc
- [Get Commit By SHA](#get-commit-by-sha)
- [Add Public SSH Key](#add-public-ssh-key)
- [Get Repository Info](#get-repository-info)
- [Get Repository Environment Info](#get-repository-environment-info)
- [Create a label](#create-a-label)
- [Get a label](#get-a-label)
- [List Pull Request Labels](#list-pull-request-labels)
Expand All @@ -66,7 +68,7 @@ apiEndpoint := "https://github.example.com"
token := "secret-github-token"
// Logger
// [Optional]
// Supported logger is a logger that implements the Log interface.
// Supported logger is a logger that implements the Log interface.
// More information - https://github.com/jfrog/froggit-go/blob/master/vcsclient/logger.go
logger := log.Default()

Expand All @@ -86,7 +88,7 @@ apiEndpoint := "https://gitlab.example.com"
token := "secret-gitlab-token"
// Logger
// [Optional]
// Supported logger is a logger that implements the Log interface.
// Supported logger is a logger that implements the Log interface.
// More information - https://github.com/jfrog/froggit-go/blob/master/vcsclient/logger.go
logger := logger

Expand All @@ -106,7 +108,7 @@ apiEndpoint := "https://git.acme.com/rest"
token := "secret-bitbucket-token"
// Logger
// [Optional]
// Supported logger is a logger that implements the Log interface.
// Supported logger is a logger that implements the Log interface.
// More information - https://github.com/jfrog/froggit-go/blob/master/vcsclient/logger.go
logger := log.Default()

Expand All @@ -128,7 +130,7 @@ username := "bitbucket-user"
token := "secret-bitbucket-token"
// Logger
// [Optional]
// Supported logger is a logger that implements the Log interface.
// Supported logger is a logger that implements the Log interface.
// More information - https://github.com/jfrog/froggit-go/blob/master/vcsclient/logger.go
logger := log.Default()

Expand All @@ -148,7 +150,7 @@ apiEndpoint := "https://dev.azure.com/<organization>"
token := "secret-azure-devops-token"
// Logger
// [Optional]
// Supported logger is a logger that implements the Log interface.
// Supported logger is a logger that implements the Log interface.
// More information - https://github.com/jfrog/froggit-go/blob/master/vcsclient/logger.go
logger := log.Default()
// Project name
Expand Down Expand Up @@ -420,6 +422,24 @@ repository := "jfrog-cli"
repoInfo, err := client.GetRepositoryInfo(ctx, owner, repository)
```

#### Get Repository Environment Info

Notice - Get Repository Environment Info is currently supported on GitHub only.

```go
// Go context
ctx := context.Background()
// Organization or username
owner := "jfrog"
// VCS repository
repository := "jfrog-cli"
// Environment name
name := "frogbot"

// Get information about repository environment
repoEnvInfo, err := client.GetRepositoryEnvironmentInfo(ctx, owner, repository, name)
```

#### Create a label

Notice - Labels are not supported in Bitbucket
Expand Down Expand Up @@ -552,4 +572,3 @@ provider := vcsutils.GitHub

webhookInfo, err := webhookparser.ParseIncomingWebhook(provider, token, request)
```

5 changes: 5 additions & 0 deletions vcsclient/azurerepos.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,8 @@ func (client *AzureReposClient) SetCommitStatus(ctx context.Context, commitStatu
func (client *AzureReposClient) DownloadFileFromRepo(ctx context.Context, owner, repository, branch, path string) ([]byte, int, error) {
return nil, 0, getUnsupportedInAzureError("download file from repo")
}

// GetRepositoryEnvironmentInfo on GitLab
func (client *AzureReposClient) GetRepositoryEnvironmentInfo(ctx context.Context, owner, repository, name string) (RepositoryEnvironmentInfo, error) {
return RepositoryEnvironmentInfo{}, getUnsupportedInAzureError("get repository environment info")
}
8 changes: 8 additions & 0 deletions vcsclient/azurerepos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,14 @@ func TestAzureReposClient_GetLabel(t *testing.T) {
assert.Error(t, err)
}

func TestAzureReposClient_GetRepositoryEnvironmentInfo(t *testing.T) {
ctx := context.Background()
client, cleanUp := createServerAndClient(t, vcsutils.AzureRepos, true, "", "unsupportedTest", createAzureReposHandler)
defer cleanUp()
_, err := client.GetRepositoryEnvironmentInfo(ctx, owner, repo1, envName)
assert.Error(t, err)
}

func TestGetUnsupportedInAzureError(t *testing.T) {
functionName := "foo"
assert.Error(t, getUnsupportedInAzureError(functionName))
Expand Down
5 changes: 5 additions & 0 deletions vcsclient/bitbucketcloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,11 @@ func (client *BitbucketCloudClient) DownloadFileFromRepo(ctx context.Context, ow
return nil, 0, errBitbucketDownloadFileFromRepoNotSupported
}

// GetRepositoryEnvironmentInfo on Bitbucket cloud
func (client *BitbucketCloudClient) GetRepositoryEnvironmentInfo(ctx context.Context, owner, repository, name string) (RepositoryEnvironmentInfo, error) {
return RepositoryEnvironmentInfo{}, errBitbucketGetRepoEnvironmentInfoNotSupported
}

func extractCommitFromResponse(commits interface{}) (*commitResponse, error) {
var res commitResponse
err := extractStructFromResponse(commits, &res)
Expand Down
9 changes: 9 additions & 0 deletions vcsclient/bitbucketcloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,15 @@ func TestBitbucketCloud_UnlabelPullRequest(t *testing.T) {
assert.ErrorIs(t, err, errLabelsNotSupported)
}

func TestBitbucketCloud_GetRepositoryEnvironmentInfo(t *testing.T) {
ctx := context.Background()
client, err := NewClientBuilder(vcsutils.BitbucketCloud).Build()
assert.NoError(t, err)

_, err = client.GetRepositoryEnvironmentInfo(ctx, owner, repo1, envName)
assert.ErrorIs(t, err, errBitbucketGetRepoEnvironmentInfoNotSupported)
}

func TestBitbucketCloud_getRepositoryVisibility(t *testing.T) {
assert.Equal(t, Private, getBitbucketCloudRepositoryVisibility(&bitbucket.Repository{Is_private: true}))
assert.Equal(t, Public, getBitbucketCloudRepositoryVisibility(&bitbucket.Repository{Is_private: false}))
Expand Down
1 change: 1 addition & 0 deletions vcsclient/bitbucketcommon.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var errLabelsNotSupported = errors.New("labels are not supported on Bitbucket")
var errBitbucketCodeScanningNotSupported = errors.New("code scanning is not supported on Bitbucket")

var errBitbucketDownloadFileFromRepoNotSupported = errors.New("download file from repo is currently not supported on Bitbucket")
var errBitbucketGetRepoEnvironmentInfoNotSupported = errors.New("get repository environment info is currently not supported on Bitbucket")

func getBitbucketCommitState(commitState CommitStatus) string {
switch commitState {
Expand Down
5 changes: 5 additions & 0 deletions vcsclient/bitbucketserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,11 @@ func (client *BitbucketServerClient) UnlabelPullRequest(ctx context.Context, own
return errLabelsNotSupported
}

// GetRepositoryEnvironmentInfo on Bitbucket server
func (client *BitbucketServerClient) GetRepositoryEnvironmentInfo(ctx context.Context, owner, repository, name string) (RepositoryEnvironmentInfo, error) {
return RepositoryEnvironmentInfo{}, errBitbucketGetRepoEnvironmentInfoNotSupported
}

// Get all projects for which the authenticated user has the PROJECT_VIEW permission
func (client *BitbucketServerClient) listProjects(bitbucketClient *bitbucketv1.DefaultApiService) ([]string, error) {
var apiResponse *bitbucketv1.APIResponse
Expand Down
9 changes: 9 additions & 0 deletions vcsclient/bitbucketserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,15 @@ func TestBitbucketServer_UnlabelPullRequest(t *testing.T) {
assert.ErrorIs(t, err, errLabelsNotSupported)
}

func TestBitbucketServer_GetRepositoryEnvironmentInfo(t *testing.T) {
ctx := context.Background()
client, err := NewClientBuilder(vcsutils.BitbucketServer).Build()
assert.NoError(t, err)

_, err = client.GetRepositoryEnvironmentInfo(ctx, owner, repo1, envName)
assert.ErrorIs(t, err, errBitbucketGetRepoEnvironmentInfoNotSupported)
}

func TestBitbucketServer_GetCommitBySha(t *testing.T) {
ctx := context.Background()
sha := "abcdef0123abcdef4567abcdef8987abcdef6543"
Expand Down
13 changes: 7 additions & 6 deletions vcsclient/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ const (
)

var (
repo1 = "repo-1"
repo2 = "repo-2"
username = "frogger"
branch1 = "branch-1"
branch2 = "branch-2"
labelName = "🚀 label-name"
repo1 = "repo-1"
repo2 = "repo-2"
username = "frogger"
branch1 = "branch-1"
branch2 = "branch-2"
labelName = "🚀 label-name"
envName = "frogbot"
)

type createHandlerFunc func(t *testing.T, expectedUri string, response []byte, expectedStatusCode int) http.HandlerFunc
Expand Down
55 changes: 55 additions & 0 deletions vcsclient/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/google/go-github/v45/github"
"github.com/grokify/mogo/encoding/base64"
"github.com/jfrog/froggit-go/vcsutils"
"github.com/mitchellh/mapstructure"
"golang.org/x/oauth2"
)

Expand Down Expand Up @@ -533,6 +534,56 @@ func (client *GitHubClient) DownloadFileFromRepo(ctx context.Context, owner, rep
return content, response.StatusCode, nil
}

// GetRepositoryEnvironmentInfo on GitHub
func (client *GitHubClient) GetRepositoryEnvironmentInfo(ctx context.Context, owner, repository, name string) (RepositoryEnvironmentInfo, error) {
err := validateParametersNotBlank(map[string]string{"owner": owner, "repository": repository, "name": name})
if err != nil {
return RepositoryEnvironmentInfo{}, err
}
ghClient, err := client.buildGithubClient(ctx)
if err != nil {
return RepositoryEnvironmentInfo{}, err
}

environment, resp, err := ghClient.Repositories.GetEnvironment(ctx, owner, repository, name)
if err != nil {
return RepositoryEnvironmentInfo{}, err
}
if err = vcsutils.CheckResponseStatusWithBody(resp.Response, http.StatusOK); err != nil {
return RepositoryEnvironmentInfo{}, err
}

reviewers, err := extractGitHubEnvironmentReviewers(environment)
if err != nil {
return RepositoryEnvironmentInfo{}, err
}

return RepositoryEnvironmentInfo{
Name: *environment.Name,
Url: *environment.URL,
Reviewers: reviewers,
}, err
}

// Extract code reviewers from environment
func extractGitHubEnvironmentReviewers(environment *github.Environment) ([]string, error) {
var reviewers []string
protectionRules := environment.ProtectionRules
if protectionRules == nil {
return reviewers, nil
}
reviewerStruct := repositoryEnvironmentReviewer{}
for _, rule := range protectionRules {
for _, reviewer := range rule.Reviewers {
if err := mapstructure.Decode(reviewer.Reviewer, &reviewerStruct); err != nil {
return []string{}, err
}
reviewers = append(reviewers, reviewerStruct.Login)
}
}
return reviewers, nil
}

func createGitHubHook(token, payloadURL string, webhookEvents ...vcsutils.WebhookEvent) *github.Hook {
return &github.Hook{
Events: getGitHubWebhookEvents(webhookEvents...),
Expand Down Expand Up @@ -636,3 +687,7 @@ func packScanningResult(data string) (string, error) {

return compressedScan, err
}

type repositoryEnvironmentReviewer struct {
Login string `mapstructure:"login"`
}
34 changes: 34 additions & 0 deletions vcsclient/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,40 @@ func TestGitHubClient_UploadScanningAnalysis(t *testing.T) {
assert.Error(t, err)
}

func TestGitHubClient_GetRepositoryEnvironmentInfo(t *testing.T) {
ctx := context.Background()

response, err := os.ReadFile(filepath.Join("testdata", "github", "repository_environment_response.json"))
assert.NoError(t, err)
client, cleanUp := createServerAndClient(t, vcsutils.GitHub, false, response, fmt.Sprintf("/repos/jfrog/repo-1/environments/%s", envName), createGitHubHandler)
defer cleanUp()

repositoryEnvironmentInfo, err := client.GetRepositoryEnvironmentInfo(ctx, owner, repo1, envName)
assert.NoError(t, err)
assert.Equal(t, envName, repositoryEnvironmentInfo.Name)
assert.Equal(t, "https://api.github.com/repos/superfrog/test-repo/environments/frogbot", repositoryEnvironmentInfo.Url)
assert.Equal(t, []string{"superfrog"}, repositoryEnvironmentInfo.Reviewers)

_, err = createBadGitHubClient(t).GetRepositoryEnvironmentInfo(ctx, owner, repo1, envName)
assert.Error(t, err)
}

func TestGitHubClient_ExtractGitHubEnvironmentReviewers(t *testing.T) {
reviewer1, reviewer2 := "reviewer-1", "reviewer-2"
environment := &github.Environment{
ProtectionRules: []*github.ProtectionRule{{
Reviewers: []*github.RequiredReviewer{
{Reviewer: &repositoryEnvironmentReviewer{Login: reviewer1}},
{Reviewer: &repositoryEnvironmentReviewer{Login: reviewer2}},
},
}},
}

actualReviewers, err := extractGitHubEnvironmentReviewers(environment)
assert.NoError(t, err)
assert.Equal(t, []string{reviewer1, reviewer2}, actualReviewers)
}

func createBadGitHubClient(t *testing.T) VcsClient {
client, err := NewClientBuilder(vcsutils.GitHub).ApiEndpoint("https://bad^endpoint").Build()
require.NoError(t, err)
Expand Down
6 changes: 6 additions & 0 deletions vcsclient/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,10 +386,16 @@ func (client *GitLabClient) UnlabelPullRequest(ctx context.Context, owner, repos
return err
}

// UploadCodeScanning on GitLab
func (client *GitLabClient) UploadCodeScanning(_ context.Context, _ string, _ string, _ string, _ string) (string, error) {
return "", errGitLabCodeScanningNotSupported
}

// GetRepositoryEnvironmentInfo on GitLab
func (client *GitLabClient) GetRepositoryEnvironmentInfo(ctx context.Context, owner, repository, name string) (RepositoryEnvironmentInfo, error) {
return RepositoryEnvironmentInfo{}, errGitLabGetRepoEnvironmentInfoNotSupported
}

// DownloadFileFromRepo on GitLab
func (client *GitLabClient) DownloadFileFromRepo(_ context.Context, owner, repository, branch, path string) ([]byte, int, error) {
file, response, err := client.glClient.RepositoryFiles.GetFile(getProjectID(owner, repository), path, &gitlab.GetFileOptions{Ref: &branch})
Expand Down
9 changes: 9 additions & 0 deletions vcsclient/gitlab_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,15 @@ func TestGitlabClient_UploadCodeScanning(t *testing.T) {
assert.Error(t, err)
}

func TestGitlabClient_GetRepositoryEnvironmentInfo(t *testing.T) {
ctx := context.Background()
client, cleanUp := createServerAndClient(t, vcsutils.GitLab, true, "", "unsupportedTest", createGitLabHandler)
defer cleanUp()

_, err := client.GetRepositoryEnvironmentInfo(ctx, owner, repo1, envName)
assert.ErrorIs(t, err, errGitLabGetRepoEnvironmentInfoNotSupported)
}

func createGitLabHandler(t *testing.T, expectedURI string, response []byte, expectedStatusCode int) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.RequestURI == "/api/v4/" {
Expand Down
1 change: 1 addition & 0 deletions vcsclient/gitlabcommon.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ import (
)

var errGitLabCodeScanningNotSupported = errors.New("code scanning is not supported on Gitlab")
var errGitLabGetRepoEnvironmentInfoNotSupported = errors.New("get repository environment info is currently not supported on Bitbucket")
42 changes: 42 additions & 0 deletions vcsclient/testdata/github/repository_environment_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"id": 458044593,
"node_id": "EN_kwDOFo0fF84bTTSx",
"name": "frogbot",
"url": "https://api.github.com/repos/superfrog/test-repo/environments/frogbot",
"html_url": "https://github.com/superfrog/test-repo/deployments/activity_log?environments_filter=frogbot",
"created_at": "2022-04-07T05:57:50Z",
"updated_at": "2022-04-07T05:57:50Z",
"protection_rules": [
{
"id": 210348,
"node_id": "GA_kwDOFo0fF84AAzWs",
"type": "required_reviewers",
"reviewers": [
{
"type": "User",
"reviewer": {
"login": "superfrog",
"id": 11367982,
"node_id": "MDQ6VXNlcjExMzY3OTgy",
"avatar_url": "https://avatars.githubusercontent.com/u/11367982?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/superfrog",
"html_url": "https://github.com/superfrog",
"followers_url": "https://api.github.com/users/superfrog/followers",
"following_url": "https://api.github.com/users/superfrog/following{/other_user}",
"gists_url": "https://api.github.com/users/superfrog/gists{/gist_id}",
"starred_url": "https://api.github.com/users/superfrog/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/superfrog/subscriptions",
"organizations_url": "https://api.github.com/users/superfrog/orgs",
"repos_url": "https://api.github.com/users/superfrog/repos",
"events_url": "https://api.github.com/users/superfrog/events{/privacy}",
"received_events_url": "https://api.github.com/users/superfrog/received_events",
"type": "User",
"site_admin": false
}
}
]
}
],
"deployment_branch_policy": null
}

0 comments on commit d41c3fc

Please sign in to comment.