github.com/dcarley/cf-cli@v6.24.1-0.20170220111324-4225ff346898+incompatible/api/cloudcontroller/ccv2/errors.go (about)

     1  package ccv2
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"code.cloudfoundry.org/cli/api/cloudcontroller"
     9  )
    10  
    11  // CCErrorResponse represents a generic Cloud Controller V2 error response.
    12  type CCErrorResponse struct {
    13  	Code        int    `json:"code"`
    14  	Description string `json:"description"`
    15  	ErrorCode   string `json:"error_code"`
    16  }
    17  
    18  // UnexpectedResponseError is returned when the client gets an error that has
    19  // not been accounted for.
    20  type UnexpectedResponseError struct {
    21  	CCErrorResponse
    22  
    23  	RequestIDs   []string
    24  	ResponseCode int
    25  }
    26  
    27  func (e UnexpectedResponseError) Error() string {
    28  	message := fmt.Sprintf("Unexpected Response\nResponse code: %d\nCC code:       %d\nCC error code: %s", e.ResponseCode, e.Code, e.ErrorCode)
    29  	for _, id := range e.RequestIDs {
    30  		message = fmt.Sprintf("%s\nRequest ID:    %s", message, id)
    31  	}
    32  	return fmt.Sprintf("%s\nDescription:   %s", message, e.Description)
    33  }
    34  
    35  // AppStoppedStatsError is returned when requesting instance information from a
    36  // stopped app.
    37  type AppStoppedStatsError struct {
    38  	Message string
    39  }
    40  
    41  func (e AppStoppedStatsError) Error() string {
    42  	return e.Message
    43  }
    44  
    45  // NotStagedError is returned when requesting instance information from a
    46  // not staged app.
    47  type NotStagedError struct {
    48  	Message string
    49  }
    50  
    51  func (e NotStagedError) Error() string {
    52  	return e.Message
    53  }
    54  
    55  // InstancesError is returned when requesting instance information encounters
    56  // an error.
    57  type InstancesError struct {
    58  	Message string
    59  }
    60  
    61  func (e InstancesError) Error() string {
    62  	return e.Message
    63  }
    64  
    65  // errorWrapper is the wrapper that converts responses with 4xx and 5xx status
    66  // codes to an error.
    67  type errorWrapper struct {
    68  	connection cloudcontroller.Connection
    69  }
    70  
    71  func newErrorWrapper() *errorWrapper {
    72  	return new(errorWrapper)
    73  }
    74  
    75  // Wrap wraps a Cloud Controller connection in this error handling wrapper.
    76  func (e *errorWrapper) Wrap(innerconnection cloudcontroller.Connection) cloudcontroller.Connection {
    77  	e.connection = innerconnection
    78  	return e
    79  }
    80  
    81  // Make converts RawHTTPStatusError, which represents responses with 4xx and
    82  // 5xx status codes, to specific errors.
    83  func (e *errorWrapper) Make(request *http.Request, passedResponse *cloudcontroller.Response) error {
    84  	err := e.connection.Make(request, passedResponse)
    85  
    86  	if rawHTTPStatusErr, ok := err.(cloudcontroller.RawHTTPStatusError); ok {
    87  		return convert(rawHTTPStatusErr)
    88  	}
    89  	return err
    90  }
    91  
    92  func convert(rawHTTPStatusErr cloudcontroller.RawHTTPStatusError) error {
    93  	// Try to unmarshal the raw error into a CC error. If unmarshaling fails,
    94  	// return the raw error.
    95  	var errorResponse CCErrorResponse
    96  	err := json.Unmarshal(rawHTTPStatusErr.RawResponse, &errorResponse)
    97  	if err != nil {
    98  		if rawHTTPStatusErr.StatusCode == http.StatusNotFound {
    99  			return cloudcontroller.NotFoundError{Message: string(rawHTTPStatusErr.RawResponse)}
   100  		}
   101  		return rawHTTPStatusErr
   102  	}
   103  
   104  	switch rawHTTPStatusErr.StatusCode {
   105  	case http.StatusBadRequest: // 400
   106  		return handleBadRequest(errorResponse)
   107  	case http.StatusUnauthorized: // 401
   108  		return handleUnauthorized(errorResponse)
   109  	case http.StatusForbidden: // 403
   110  		return cloudcontroller.ForbiddenError{Message: errorResponse.Description}
   111  	case http.StatusNotFound: // 404
   112  		return cloudcontroller.ResourceNotFoundError{Message: errorResponse.Description}
   113  	case http.StatusUnprocessableEntity: // 422
   114  		return cloudcontroller.UnprocessableEntityError{Message: errorResponse.Description}
   115  	default:
   116  		return UnexpectedResponseError{
   117  			CCErrorResponse: errorResponse,
   118  			RequestIDs:      rawHTTPStatusErr.RequestIDs,
   119  			ResponseCode:    rawHTTPStatusErr.StatusCode,
   120  		}
   121  	}
   122  	return nil
   123  }
   124  
   125  func handleBadRequest(errorResponse CCErrorResponse) error {
   126  	switch errorResponse.ErrorCode {
   127  	case "CF-AppStoppedStatsError":
   128  		return AppStoppedStatsError{Message: errorResponse.Description}
   129  	case "CF-InstancesError":
   130  		return InstancesError{Message: errorResponse.Description}
   131  	case "CF-NotStaged":
   132  		return NotStagedError{Message: errorResponse.Description}
   133  	default:
   134  		return cloudcontroller.BadRequestError{Message: errorResponse.Description}
   135  	}
   136  }
   137  
   138  func handleUnauthorized(errorResponse CCErrorResponse) error {
   139  	if errorResponse.ErrorCode == "CF-InvalidAuthToken" {
   140  		return cloudcontroller.InvalidAuthTokenError{Message: errorResponse.Description}
   141  	}
   142  
   143  	return cloudcontroller.UnauthorizedError{Message: errorResponse.Description}
   144  }