Merge branch 'trunk' of github.com:cli/cli into jg/choose-codespace-prompt
This commit is contained in:
commit
811d841fae
2 changed files with 121 additions and 20 deletions
|
|
@ -34,6 +34,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -187,15 +188,43 @@ type CodespaceEnvironmentConnection struct {
|
||||||
HostPublicKeys []string `json:"hostPublicKeys"`
|
HostPublicKeys []string `json:"hostPublicKeys"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) ListCodespaces(ctx context.Context) ([]*Codespace, error) {
|
// codespacesListResponse is the response body for the `/user/codespaces` endpoint
|
||||||
|
type getCodespacesListResponse struct {
|
||||||
|
Codespaces []*Codespace `json:"codespaces"`
|
||||||
|
TotalCount int `json:"total_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListCodespaces returns a list of codespaces for the user.
|
||||||
|
// It consumes all pages returned by the API until all codespaces have been fetched.
|
||||||
|
func (a *API) ListCodespaces(ctx context.Context) (codespaces []*Codespace, err error) {
|
||||||
|
per_page := 100
|
||||||
|
for page := 1; ; page++ {
|
||||||
|
response, err := a.fetchCodespaces(ctx, page, per_page)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
codespaces = append(codespaces, response.Codespaces...)
|
||||||
|
if page*per_page >= response.TotalCount {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return codespaces, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) fetchCodespaces(ctx context.Context, page int, per_page int) (response *getCodespacesListResponse, err error) {
|
||||||
req, err := http.NewRequest(
|
req, err := http.NewRequest(
|
||||||
http.MethodGet, a.githubAPI+"/user/codespaces", nil,
|
http.MethodGet, a.githubAPI+"/user/codespaces", nil,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating request: %w", err)
|
return nil, fmt.Errorf("error creating request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
a.setHeaders(req)
|
a.setHeaders(req)
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("page", strconv.Itoa(page))
|
||||||
|
q.Add("per_page", strconv.Itoa(per_page))
|
||||||
|
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
resp, err := a.do(ctx, req, "/user/codespaces")
|
resp, err := a.do(ctx, req, "/user/codespaces")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error making request: %w", err)
|
return nil, fmt.Errorf("error making request: %w", err)
|
||||||
|
|
@ -211,13 +240,10 @@ func (a *API) ListCodespaces(ctx context.Context) ([]*Codespace, error) {
|
||||||
return nil, jsonErrorResponse(b)
|
return nil, jsonErrorResponse(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
var response struct {
|
|
||||||
Codespaces []*Codespace `json:"codespaces"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(b, &response); err != nil {
|
if err := json.Unmarshal(b, &response); err != nil {
|
||||||
return nil, fmt.Errorf("error unmarshaling response: %w", err)
|
return nil, fmt.Errorf("error unmarshaling response: %w", err)
|
||||||
}
|
}
|
||||||
return response.Codespaces, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCodespace returns the user codespace based on the provided name.
|
// GetCodespace returns the user codespace based on the provided name.
|
||||||
|
|
|
||||||
|
|
@ -6,26 +6,61 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestListCodespaces(t *testing.T) {
|
func generateCodespaceList(start int, end int) []*Codespace {
|
||||||
codespaces := []*Codespace{
|
codespacesList := []*Codespace{}
|
||||||
{
|
for i := start; i < end; i++ {
|
||||||
Name: "testcodespace",
|
codespacesList = append(codespacesList, &Codespace{
|
||||||
CreatedAt: "2021-08-09T10:10:24+02:00",
|
Name: fmt.Sprintf("codespace-%d", i),
|
||||||
LastUsedAt: "2021-08-09T13:10:24+02:00",
|
})
|
||||||
},
|
|
||||||
}
|
}
|
||||||
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return codespacesList
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFakeListEndpointServer(t *testing.T, initalTotal int, finalTotal int) *httptest.Server {
|
||||||
|
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.Path != "/user/codespaces" {
|
||||||
|
t.Fatal("Incorrect path")
|
||||||
|
}
|
||||||
|
|
||||||
|
page := 1
|
||||||
|
if r.URL.Query().Get("page") != "" {
|
||||||
|
page, _ = strconv.Atoi(r.URL.Query().Get("page"))
|
||||||
|
}
|
||||||
|
|
||||||
|
per_page := 0
|
||||||
|
if r.URL.Query().Get("per_page") != "" {
|
||||||
|
per_page, _ = strconv.Atoi(r.URL.Query().Get("per_page"))
|
||||||
|
}
|
||||||
|
|
||||||
response := struct {
|
response := struct {
|
||||||
Codespaces []*Codespace `json:"codespaces"`
|
Codespaces []*Codespace `json:"codespaces"`
|
||||||
|
TotalCount int `json:"total_count"`
|
||||||
}{
|
}{
|
||||||
Codespaces: codespaces,
|
Codespaces: []*Codespace{},
|
||||||
|
TotalCount: finalTotal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if page == 1 {
|
||||||
|
response.Codespaces = generateCodespaceList(0, per_page)
|
||||||
|
response.TotalCount = initalTotal
|
||||||
|
} else if page == 2 {
|
||||||
|
response.Codespaces = generateCodespaceList(per_page, per_page*2)
|
||||||
|
response.TotalCount = finalTotal
|
||||||
|
} else {
|
||||||
|
t.Fatal("Should not check extra page")
|
||||||
|
}
|
||||||
|
|
||||||
data, _ := json.Marshal(response)
|
data, _ := json.Marshal(response)
|
||||||
fmt.Fprint(w, string(data))
|
fmt.Fprint(w, string(data))
|
||||||
}))
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestListCodespaces(t *testing.T) {
|
||||||
|
svr := createFakeListEndpointServer(t, 200, 200)
|
||||||
defer svr.Close()
|
defer svr.Close()
|
||||||
|
|
||||||
api := API{
|
api := API{
|
||||||
|
|
@ -38,13 +73,53 @@ func TestListCodespaces(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if len(codespaces) != 200 {
|
||||||
if len(codespaces) != 1 {
|
t.Fatalf("expected 100 codespace, got %d", len(codespaces))
|
||||||
t.Fatalf("expected 1 codespace, got %d", len(codespaces))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if codespaces[0].Name != "testcodespace" {
|
if codespaces[0].Name != "codespace-0" {
|
||||||
t.Fatalf("expected testcodespace, got %s", codespaces[0].Name)
|
t.Fatalf("expected codespace-0, got %s", codespaces[0].Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if codespaces[199].Name != "codespace-199" {
|
||||||
|
t.Fatalf("expected codespace-199, got %s", codespaces[0].Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMidIterationDeletion(t *testing.T) {
|
||||||
|
svr := createFakeListEndpointServer(t, 200, 199)
|
||||||
|
defer svr.Close()
|
||||||
|
|
||||||
|
api := API{
|
||||||
|
githubAPI: svr.URL,
|
||||||
|
client: &http.Client{},
|
||||||
|
token: "faketoken",
|
||||||
|
}
|
||||||
|
ctx := context.TODO()
|
||||||
|
codespaces, err := api.ListCodespaces(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(codespaces) != 200 {
|
||||||
|
t.Fatalf("expected 200 codespace, got %d", len(codespaces))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMidIterationAddition(t *testing.T) {
|
||||||
|
svr := createFakeListEndpointServer(t, 199, 200)
|
||||||
|
defer svr.Close()
|
||||||
|
|
||||||
|
api := API{
|
||||||
|
githubAPI: svr.URL,
|
||||||
|
client: &http.Client{},
|
||||||
|
token: "faketoken",
|
||||||
|
}
|
||||||
|
ctx := context.TODO()
|
||||||
|
codespaces, err := api.ListCodespaces(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(codespaces) != 200 {
|
||||||
|
t.Fatalf("expected 200 codespace, got %d", len(codespaces))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue