Change the way we parse list-devcontainers response

This commit is contained in:
Jeff Hubbard 2022-03-28 14:24:57 -07:00
parent 03b8b16585
commit 9554e522af
7 changed files with 51 additions and 28 deletions

View file

@ -762,9 +762,14 @@ func (a *API) DeleteCodespace(ctx context.Context, codespaceName string) error {
return nil
}
type DevContainerEntry struct {
Path string `json:"path"`
Name string `json:"name,omitempty"`
}
// ListDevContainers returns a list of valid devcontainer.json files for the repo. Pass a negative limit to request all pages from
// the API until all devcontainer.json files have been fetched.
func (a *API) ListDevContainers(ctx context.Context, repoID int, branch string, limit int) (devcontainers []string, err error) {
func (a *API) ListDevContainers(ctx context.Context, repoID int, branch string, limit int) (devcontainers []DevContainerEntry, err error) {
perPage := 100
if limit > 0 && limit < 100 {
perPage = limit
@ -792,8 +797,9 @@ func (a *API) ListDevContainers(ctx context.Context, repoID int, branch string,
}
var response struct {
Devcontainers []string `json:"devcontainers"`
Devcontainers []DevContainerEntry `json:"devcontainers"`
}
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&response); err != nil {
return nil, fmt.Errorf("error unmarshaling response: %w", err)

View file

@ -74,7 +74,7 @@ type apiClient interface {
GetCodespaceRegionLocation(ctx context.Context) (string, error)
GetCodespacesMachines(ctx context.Context, repoID int, branch, location string) ([]*api.Machine, error)
GetCodespaceRepositoryContents(ctx context.Context, codespace *api.Codespace, path string) ([]byte, error)
ListDevContainers(ctx context.Context, repoID int, branch string, limit int) (devcontainers []string, err error)
ListDevContainers(ctx context.Context, repoID int, branch string, limit int) (devcontainers []api.DevContainerEntry, err error)
GetCodespaceRepoSuggestions(ctx context.Context, partialSearch string, params api.RepoSearchParameters) ([]string, error)
}

View file

@ -58,6 +58,8 @@ func (a *App) Create(ctx context.Context, opts createOptions) error {
locationCh := getLocation(ctx, vscsLocation, a.apiClient)
DEFAULT_DEVCONTAINER_DEFINITIONS := []string{".devcontainer.json", ".devcontainer/devcontainer.json"}
userInputs := struct {
Repository string
Branch string
@ -113,23 +115,35 @@ func (a *App) Create(ctx context.Context, opts createOptions) error {
// now that we have repo+branch, we can list available devcontainer.json files (if any)
if len(opts.devContainerPath) < 1 {
a.StartProgressIndicatorWithLabel("Fetching devcontainer.json files")
devContainerPaths, err := a.apiClient.ListDevContainers(ctx, repository.ID, branch, 100)
devcontainers, err := a.apiClient.ListDevContainers(ctx, repository.ID, branch, 100)
if err != nil {
return fmt.Errorf("error getting devcontainer.json paths: %w", err)
}
a.StopProgressIndicator()
if len(devContainerPaths) > 0 {
devContainerPathQuestion := &survey.Question{
Name: "devContainerPath",
Prompt: &survey.Select{
Message: "Devcontainer definition file:",
Options: append([]string{"default"}, devContainerPaths...),
},
}
if len(devcontainers) > 0 {
if err := ask([]*survey.Question{devContainerPathQuestion}, &devContainerPath); err != nil {
return fmt.Errorf("failed to prompt: %w", err)
// if there is only one devcontainer.json file and it is one of the default paths we can auto-select it
if len(devcontainers) == 1 && utils.StringInSlice(devcontainers[0].Path, DEFAULT_DEVCONTAINER_DEFINITIONS) {
devContainerPath = devcontainers[0].Path
} else {
promptOptions := []string{"default"}
for _, devcontainer := range devcontainers {
promptOptions = append(promptOptions, devcontainer.Path)
}
devContainerPathQuestion := &survey.Question{
Name: "devContainerPath",
Prompt: &survey.Select{
Message: "Devcontainer definition file:",
Options: promptOptions,
},
}
if err := ask([]*survey.Question{devContainerPathQuestion}, &devContainerPath); err != nil {
return fmt.Errorf("failed to prompt: %w", err)
}
}
}

View file

@ -86,8 +86,8 @@ func TestApp_Create(t *testing.T) {
DefaultBranch: "main",
}, nil
},
ListDevContainersFunc: func(ctx context.Context, repoID int, branch string, limit int) ([]string, error) {
return []string{}, nil
ListDevContainersFunc: func(ctx context.Context, repoID int, branch string, limit int) ([]api.DevContainerEntry, error) {
return []api.DevContainerEntry{}, nil
},
GetCodespacesMachinesFunc: func(ctx context.Context, repoID int, branch, location string) ([]*api.Machine, error) {
return []*api.Machine{
@ -139,7 +139,7 @@ func TestApp_Create(t *testing.T) {
DefaultBranch: "main",
}, nil
},
ListDevContainersFunc: func(ctx context.Context, repoID int, branch string, limit int) ([]string, error) {
ListDevContainersFunc: func(ctx context.Context, repoID int, branch string, limit int) ([]api.DevContainerEntry, error) {
return nil, fmt.Errorf("some error")
},
},
@ -167,8 +167,8 @@ func TestApp_Create(t *testing.T) {
DefaultBranch: "main",
}, nil
},
ListDevContainersFunc: func(ctx context.Context, repoID int, branch string, limit int) ([]string, error) {
return []string{}, nil
ListDevContainersFunc: func(ctx context.Context, repoID int, branch string, limit int) ([]api.DevContainerEntry, error) {
return []api.DevContainerEntry{}, nil
},
GetCodespacesMachinesFunc: func(ctx context.Context, repoID int, branch, location string) ([]*api.Machine, error) {
return []*api.Machine{

View file

@ -52,7 +52,7 @@ import (
// ListCodespacesFunc: func(ctx context.Context, limit int) ([]*api.Codespace, error) {
// panic("mock out the ListCodespaces method")
// },
// ListDevContainersFunc: func(ctx context.Context, repoID int, branch string, limit int) ([]string, error) {
// ListDevContainersFunc: func(ctx context.Context, repoID int, branch string, limit int) ([]api.DevContainerEntry, error) {
// panic("mock out the ListDevContainers method")
// },
// StartCodespaceFunc: func(ctx context.Context, name string) error {
@ -105,7 +105,7 @@ type apiClientMock struct {
ListCodespacesFunc func(ctx context.Context, limit int) ([]*api.Codespace, error)
// ListDevContainersFunc mocks the ListDevContainers method.
ListDevContainersFunc func(ctx context.Context, repoID int, branch string, limit int) ([]string, error)
ListDevContainersFunc func(ctx context.Context, repoID int, branch string, limit int) ([]api.DevContainerEntry, error)
// StartCodespaceFunc mocks the StartCodespace method.
StartCodespaceFunc func(ctx context.Context, name string) error
@ -687,7 +687,7 @@ func (mock *apiClientMock) ListCodespacesCalls() []struct {
}
// ListDevContainers calls ListDevContainersFunc.
func (mock *apiClientMock) ListDevContainers(ctx context.Context, repoID int, branch string, limit int) ([]string, error) {
func (mock *apiClientMock) ListDevContainers(ctx context.Context, repoID int, branch string, limit int) ([]api.DevContainerEntry, error) {
if mock.ListDevContainersFunc == nil {
panic("apiClientMock.ListDevContainersFunc: method is nil but apiClient.ListDevContainers was just called")
}

View file

@ -1,6 +0,0 @@
{
"repository_id": 392831291,
"location": "WestUs2",
"devcontainer_path": ".devcontainer/foo ' \" bar/devcontainer.json",
"vscs_target": "development"
}

View file

@ -83,3 +83,12 @@ func DisplayURL(urlStr string) string {
func ValidURL(urlStr string) bool {
return len(urlStr) < 8192
}
func StringInSlice(a string, slice []string) bool {
for _, b := range slice {
if b == a {
return true
}
}
return false
}