-
Notifications
You must be signed in to change notification settings - Fork 237
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- `circleci project list-secrets` Other changes: - Replace a request header `Accept-Type` with `Accept`
- Loading branch information
1 parent
c849191
commit 5cbf90e
Showing
8 changed files
with
498 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package project | ||
|
||
import ( | ||
projectapi "github.com/CircleCI-Public/circleci-cli/api/project" | ||
"github.com/CircleCI-Public/circleci-cli/cmd/validator" | ||
"github.com/olekukonko/tablewriter" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func newProjectEnvironmentVariableCommand(ops *projectOpts, preRunE validator.Validator) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "secret", | ||
Short: "Operate on environment variables of projects", | ||
} | ||
|
||
listVarsCommand := &cobra.Command{ | ||
Short: "List all environment variables of a project", | ||
Use: "list <vcs-type> <org-name> <project-name>", | ||
PreRunE: preRunE, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return listProjectEnvironmentVariables(cmd, ops.client, args[0], args[1], args[2]) | ||
}, | ||
Args: cobra.ExactArgs(3), | ||
} | ||
|
||
cmd.AddCommand(listVarsCommand) | ||
return cmd | ||
} | ||
|
||
func listProjectEnvironmentVariables(cmd *cobra.Command, client projectapi.ProjectClient, vcsType, orgName, projName string) error { | ||
envVars, err := client.ListAllEnvironmentVariables(vcsType, orgName, projName) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
table := tablewriter.NewWriter(cmd.OutOrStdout()) | ||
|
||
table.SetHeader([]string{"Environment Variable", "Value"}) | ||
|
||
for _, envVar := range envVars { | ||
table.Append([]string{envVar.Name, envVar.Value}) | ||
} | ||
table.Render() | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package project | ||
|
||
import ( | ||
projectapi "github.com/CircleCI-Public/circleci-cli/api/project" | ||
"github.com/CircleCI-Public/circleci-cli/cmd/validator" | ||
|
||
"github.com/CircleCI-Public/circleci-cli/settings" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
type projectOpts struct { | ||
client projectapi.ProjectClient | ||
} | ||
|
||
// NewProjectCommand generates a cobra command for managing projects | ||
func NewProjectCommand(config *settings.Config, preRunE validator.Validator) *cobra.Command { | ||
var opts projectOpts | ||
command := &cobra.Command{ | ||
Use: "project", | ||
Short: "Operate on projects", | ||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { | ||
client, err := projectapi.NewProjectRestClient(*config) | ||
if err != nil { | ||
return err | ||
} | ||
opts.client = client | ||
return nil | ||
}, | ||
} | ||
|
||
command.AddCommand(newProjectEnvironmentVariableCommand(&opts, preRunE)) | ||
|
||
return command | ||
} |
Oops, something went wrong.