Skip to content

Commit

Permalink
Consistent error handling in codespaces API operations
Browse files Browse the repository at this point in the history
Using HandleHTTPError ensures that hints regarding insufficient OAuth
scopes will be properly reported on stderr.
  • Loading branch information
mislav committed Oct 14, 2021
1 parent 2c3f02e commit 693193f
Showing 1 changed file with 35 additions and 68 deletions.
103 changes: 35 additions & 68 deletions internal/codespaces/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ func (a *API) GetUser(ctx context.Context) (*User, error) {
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, api.HandleHTTPError(resp)
}

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}

if resp.StatusCode != http.StatusOK {
return nil, jsonErrorResponse(b)
}

var response User
if err := json.Unmarshal(b, &response); err != nil {
return nil, fmt.Errorf("error unmarshaling response: %w", err)
Expand All @@ -102,18 +102,6 @@ func (a *API) GetUser(ctx context.Context) (*User, error) {
return &response, nil
}

// jsonErrorResponse returns the error message from a JSON response.
func jsonErrorResponse(b []byte) error {
var response struct {
Message string `json:"message"`
}
if err := json.Unmarshal(b, &response); err != nil {
return fmt.Errorf("error unmarshaling error response: %w", err)
}

return errors.New(response.Message)
}

// Repository represents a GitHub repository.
type Repository struct {
ID int `json:"id"`
Expand All @@ -133,15 +121,15 @@ func (a *API) GetRepository(ctx context.Context, nwo string) (*Repository, error
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, api.HandleHTTPError(resp)
}

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}

if resp.StatusCode != http.StatusOK {
return nil, jsonErrorResponse(b)
}

var response Repository
if err := json.Unmarshal(b, &response); err != nil {
return nil, fmt.Errorf("error unmarshaling response: %w", err)
Expand Down Expand Up @@ -286,15 +274,15 @@ func (a *API) GetCodespace(ctx context.Context, codespaceName string, includeCon
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, api.HandleHTTPError(resp)
}

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}

if resp.StatusCode != http.StatusOK {
return nil, jsonErrorResponse(b)
}

var response Codespace
if err := json.Unmarshal(b, &response); err != nil {
return nil, fmt.Errorf("error unmarshaling response: %w", err)
Expand Down Expand Up @@ -322,26 +310,12 @@ func (a *API) StartCodespace(ctx context.Context, codespaceName string) error {
}
defer resp.Body.Close()

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("error reading response body: %w", err)
}

if resp.StatusCode != http.StatusOK {
if resp.StatusCode == http.StatusConflict {
// 409 means the codespace is already running which we can safely ignore
return nil
}

// Error response may be a numeric code or a JSON {"message": "..."}.
if bytes.HasPrefix(b, []byte("{")) {
return jsonErrorResponse(b) // probably JSON
}

if len(b) > 100 {
b = append(b[:97], "..."...)
}
return fmt.Errorf("failed to start codespace: %s (%s)", b, resp.Status)
return api.HandleHTTPError(resp)
}

return nil
Expand All @@ -364,15 +338,15 @@ func (a *API) GetCodespaceRegionLocation(ctx context.Context) (string, error) {
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return "", api.HandleHTTPError(resp)
}

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("error reading response body: %w", err)
}

if resp.StatusCode != http.StatusOK {
return "", jsonErrorResponse(b)
}

var response getCodespaceRegionLocationResponse
if err := json.Unmarshal(b, &response); err != nil {
return "", fmt.Errorf("error unmarshaling response: %w", err)
Expand Down Expand Up @@ -406,15 +380,15 @@ func (a *API) GetCodespacesMachines(ctx context.Context, repoID int, branch, loc
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, api.HandleHTTPError(resp)
}

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}

if resp.StatusCode != http.StatusOK {
return nil, jsonErrorResponse(b)
}

var response struct {
Machines []*Machine `json:"machines"`
}
Expand Down Expand Up @@ -499,18 +473,17 @@ func (a *API) startCreate(ctx context.Context, repoID int, machine, branch, loca
}
defer resp.Body.Close()

if resp.StatusCode == http.StatusAccepted {
return nil, errProvisioningInProgress // RPC finished before result of creation known
} else if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
return nil, api.HandleHTTPError(resp)
}

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}

switch {
case resp.StatusCode > http.StatusAccepted:
return nil, jsonErrorResponse(b)
case resp.StatusCode == http.StatusAccepted:
return nil, errProvisioningInProgress // RPC finished before result of creation known
}

var response Codespace
if err := json.Unmarshal(b, &response); err != nil {
return nil, fmt.Errorf("error unmarshaling response: %w", err)
Expand All @@ -533,12 +506,8 @@ func (a *API) DeleteCodespace(ctx context.Context, codespaceName string) error {
}
defer resp.Body.Close()

if resp.StatusCode > http.StatusAccepted {
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("error reading response body: %w", err)
}
return jsonErrorResponse(b)
if resp.StatusCode != http.StatusOK {
return api.HandleHTTPError(resp)
}

return nil
Expand Down Expand Up @@ -567,17 +536,15 @@ func (a *API) GetCodespaceRepositoryContents(ctx context.Context, codespace *Cod

if resp.StatusCode == http.StatusNotFound {
return nil, nil
} else if resp.StatusCode != http.StatusOK {
return nil, api.HandleHTTPError(resp)
}

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}

if resp.StatusCode != http.StatusOK {
return nil, jsonErrorResponse(b)
}

var response getCodespaceRepositoryContentsResponse
if err := json.Unmarshal(b, &response); err != nil {
return nil, fmt.Errorf("error unmarshaling response: %w", err)
Expand Down Expand Up @@ -605,14 +572,14 @@ func (a *API) AuthorizedKeys(ctx context.Context, user string) ([]byte, error) {
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("server returned %s", resp.Status)
}

b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %w", err)
}

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("server returned %s", resp.Status)
}
return b, nil
}

Expand Down

0 comments on commit 693193f

Please sign in to comment.