Skip to content

Commit

Permalink
Merge pull request #842 from CircleCI-Public/main
Browse files Browse the repository at this point in the history
Merge main in develop
  • Loading branch information
AbrahamTewa authored Feb 17, 2023
2 parents 99feff3 + 901243e commit df055ca
Show file tree
Hide file tree
Showing 15 changed files with 577 additions and 42 deletions.
4 changes: 2 additions & 2 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
* @CircleCI-Public/Extensibility
*orb*.go @CircleCI-Public/CPEng @CircleCI-Public/Extensibility
* @CircleCI-Public/developer-experience
*orb*.go @CircleCI-Public/CPEng @CircleCI-Public/developer-experience

/api/runner @CircleCI-Public/runner
/cmd/runner @CircleCI-Public/runner
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@ You can also set a specific version of the CLI to install with the `VERSION` env
curl -fLSs https://raw.githubusercontent.com/CircleCI-Public/circleci-cli/main/install.sh | VERSION=0.1.5222 sudo bash
```

#### Checksum verification

If you would like to verify the checksum yourself, you can download the checksum file from the [GitHub releases page](https://github.com/CircleCI-Public/circleci-cli/releases) and verify the checksum of the archive using the `circleci-cli_<version>_checksums.txt` inside the assets of the release you'd like to install:

On macOS and Linux:
```sh
shasum -a 256 circleci-cli_<version>_<os>.tar.gz
```

and on Windows:
```powershell
Get-FileHash .\circleci-cli_<version>_<os>.tar.gz -Algorithm SHA256 | Format-List
```

And compare it to the right checksum depending on the downloaded version in the `circleci-cli_<version>_checksums.txt` file.

### Updating

If you installed the CLI without a package manager, you can use its built-in update command to check for pending updates and download them:
Expand Down
9 changes: 5 additions & 4 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, pa
fieldAddendums)

request := graphql.NewRequest(query)
request.SetToken(cl.Token)
request.Var("config", config)

if values != nil {
Expand All @@ -560,8 +561,6 @@ func ConfigQueryLegacy(cl *graphql.Client, configPath string, orgSlug string, pa
request.Var("orgSlug", orgSlug)
}

request.SetToken(cl.Token)

err = cl.Run(request, &response)
if err != nil {
return nil, errors.Wrap(err, "Unable to validate config")
Expand Down Expand Up @@ -603,6 +602,7 @@ func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pip
fieldAddendums)

request := graphql.NewRequest(query)
request.SetToken(cl.Token)
request.Var("config", config)

if values != nil {
Expand All @@ -619,7 +619,6 @@ func ConfigQuery(cl *graphql.Client, configPath string, orgId string, params pip
if orgId != "" {
request.Var("orgId", orgId)
}
request.SetToken(cl.Token)

err = cl.Run(request, &response)
if err != nil {
Expand Down Expand Up @@ -1634,6 +1633,7 @@ query namespaceOrbs ($namespace: String, $after: String!) {

for {
request := graphql.NewRequest(query)
request.SetToken(cl.Token)
request.Var("after", currentCursor)
request.Var("namespace", namespace)

Expand Down Expand Up @@ -1813,7 +1813,7 @@ func OrbCategoryID(cl *graphql.Client, name string) (*OrbCategoryIDResponse, err
}`

request := graphql.NewRequest(query)

request.SetToken(cl.Token)
request.Var("name", name)

err := cl.Run(request, &response)
Expand Down Expand Up @@ -1917,6 +1917,7 @@ func ListOrbCategories(cl *graphql.Client) (*OrbCategoriesForListing, error) {

for {
request := graphql.NewRequest(query)
request.SetToken(cl.Token)
request.Var("after", currentCursor)

err := cl.Run(request, &result)
Expand Down
13 changes: 13 additions & 0 deletions api/project/project.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package project

// ProjectEnvironmentVariable is a Environment Variable of a Project
type ProjectEnvironmentVariable struct {
Name string
Value string
}

// ProjectClient is the interface to interact with project and it's
// components.
type ProjectClient interface {
ListAllEnvironmentVariables(vcs, org, project string) ([]*ProjectEnvironmentVariable, error)
}
104 changes: 104 additions & 0 deletions api/project/project_rest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package project

import (
"fmt"
"net/url"

"github.com/CircleCI-Public/circleci-cli/api/rest"
"github.com/CircleCI-Public/circleci-cli/settings"
)

type projectRestClient struct {
token string
server string
client *rest.Client
}

var _ ProjectClient = &projectRestClient{}

type listProjectEnvVarsParams struct {
vcs string
org string
project string
pageToken string
}

type projectEnvVarResponse struct {
Name string
Value string
}

type listAllProjectEnvVarsResponse struct {
Items []projectEnvVarResponse
NextPageToken string `json:"next_page_token"`
}

// NewProjectRestClient returns a new projectRestClient satisfying the api.ProjectInterface
// interface via the REST API.
func NewProjectRestClient(config settings.Config) (*projectRestClient, error) {
serverURL, err := config.ServerURL()
if err != nil {
return nil, err
}

client := &projectRestClient{
token: config.Token,
server: serverURL.String(),
client: rest.New(config.Host, &config),
}

return client, nil
}

// ListAllEnvironmentVariables returns all of the environment variables owned by the
// given project. Note that pagination is not supported - we get all
// pages of env vars and return them all.
func (p *projectRestClient) ListAllEnvironmentVariables(vcs, org, project string) ([]*ProjectEnvironmentVariable, error) {
res := make([]*ProjectEnvironmentVariable, 0)
var nextPageToken string
for {
resp, err := p.listEnvironmentVariables(&listProjectEnvVarsParams{
vcs: vcs,
org: org,
project: project,
pageToken: nextPageToken,
})
if err != nil {
return nil, err
}

for _, ev := range resp.Items {
res = append(res, &ProjectEnvironmentVariable{
Name: ev.Name,
Value: ev.Value,
})
}

if resp.NextPageToken == "" {
break
}

nextPageToken = resp.NextPageToken
}
return res, nil
}

func (c *projectRestClient) listEnvironmentVariables(params *listProjectEnvVarsParams) (*listAllProjectEnvVarsResponse, error) {
path := fmt.Sprintf("project/%s/%s/%s/envvar", params.vcs, params.org, params.project)
urlParams := url.Values{}
if params.pageToken != "" {
urlParams.Add("page-token", params.pageToken)
}

req, err := c.client.NewRequest("GET", &url.URL{Path: path, RawQuery: urlParams.Encode()}, nil)
if err != nil {
return nil, err
}

var resp listAllProjectEnvVarsResponse
_, err = c.client.DoRequest(req, &resp)
if err != nil {
return nil, err
}
return &resp, nil
}
160 changes: 160 additions & 0 deletions api/project/project_rest_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package project_test

import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"testing"

"github.com/CircleCI-Public/circleci-cli/api/project"
"github.com/CircleCI-Public/circleci-cli/settings"
"github.com/CircleCI-Public/circleci-cli/version"
"gotest.tools/v3/assert"
)

func getProjectRestClient(server *httptest.Server) (project.ProjectClient, error) {
client := &http.Client{}

return project.NewProjectRestClient(settings.Config{
RestEndpoint: "api/v2",
Host: server.URL,
HTTPClient: client,
Token: "token",
})
}

func Test_projectRestClient_ListAllEnvironmentVariables(t *testing.T) {
const (
vcsType = "github"
orgName = "test-org"
projName = "test-proj"
)
tests := []struct {
name string
handler http.HandlerFunc
want []*project.ProjectEnvironmentVariable
wantErr bool
}{
{
name: "Should handle a successful request with ListAllEnvironmentVariables",
handler: func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, r.Header.Get("circle-token"), "token")
assert.Equal(t, r.Header.Get("accept"), "application/json")
assert.Equal(t, r.Header.Get("user-agent"), version.UserAgent())

assert.Equal(t, r.Method, "GET")
assert.Equal(t, r.URL.Path, fmt.Sprintf("/api/v2/project/%s/%s/%s/envvar", vcsType, orgName, projName))

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte(`
{
"items": [{
"name": "foo",
"value": "xxxx1234"
}],
"next_page_token": ""
}`))
assert.NilError(t, err)
},
want: []*project.ProjectEnvironmentVariable{
{
Name: "foo",
Value: "xxxx1234",
},
},
},
{
name: "Should handle a request containing next_page_token with ListAllEnvironmentVariables",
handler: func(w http.ResponseWriter, r *http.Request) {
u, err := url.ParseQuery(r.URL.RawQuery)
assert.NilError(t, err)

w.Header().Set("content-type", "application/json")
w.WriteHeader(http.StatusOK)
if tk := u.Get("page-token"); tk == "" {
_, err := w.Write([]byte(`
{
"items": [
{
"name": "foo1",
"value": "xxxx1234"
},
{
"name": "foo2",
"value": "xxxx2345"
}
],
"next_page_token": "pagetoken"
}`))
assert.NilError(t, err)
} else {
assert.Equal(t, tk, "pagetoken")
_, err := w.Write([]byte(`
{
"items": [
{
"name": "bar1",
"value": "xxxxabcd"
},
{
"name": "bar2",
"value": "xxxxbcde"
}
],
"next_page_token": ""
}`))
assert.NilError(t, err)
}
},
want: []*project.ProjectEnvironmentVariable{
{
Name: "foo1",
Value: "xxxx1234",
},
{
Name: "foo2",
Value: "xxxx2345",
},
{
Name: "bar1",
Value: "xxxxabcd",
},
{
Name: "bar2",
Value: "xxxxbcde",
},
},
},
{
name: "Should handle an error request with ListAllEnvironmentVariables",
handler: func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("content-type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
_, err := w.Write([]byte(`{"message": "error"}`))
assert.NilError(t, err)
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := httptest.NewServer(tt.handler)
defer server.Close()

p, err := getProjectRestClient(server)
assert.NilError(t, err)

got, err := p.ListAllEnvironmentVariables(vcsType, orgName, projName)
if (err != nil) != tt.wantErr {
t.Errorf("projectRestClient.ListAllEnvironmentVariables() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("projectRestClient.ListAllEnvironmentVariables() = %v, want %v", got, tt.want)
}
})
}
}
Loading

0 comments on commit df055ca

Please sign in to comment.