github.com/cloudfoundry-attic/cli-with-i18n@v6.32.1-0.20171002233121-7401370d3b85+incompatible/api/cloudcontroller/ccv3/errors.go (about)

     1  package ccv3
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  
     7  	"code.cloudfoundry.org/cli/api/cloudcontroller"
     8  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
     9  )
    10  
    11  // errorWrapper is the wrapper that converts responses with 4xx and 5xx status
    12  // codes to an error.
    13  type errorWrapper struct {
    14  	connection cloudcontroller.Connection
    15  }
    16  
    17  func newErrorWrapper() *errorWrapper {
    18  	return new(errorWrapper)
    19  }
    20  
    21  // Wrap wraps a Cloud Controller connection in this error handling wrapper.
    22  func (e *errorWrapper) Wrap(innerconnection cloudcontroller.Connection) cloudcontroller.Connection {
    23  	e.connection = innerconnection
    24  	return e
    25  }
    26  
    27  // Make creates a connection in the wrapped connection and handles errors
    28  // that it returns.
    29  func (e *errorWrapper) Make(request *cloudcontroller.Request, passedResponse *cloudcontroller.Response) error {
    30  	err := e.connection.Make(request, passedResponse)
    31  
    32  	if rawHTTPStatusErr, ok := err.(ccerror.RawHTTPStatusError); ok {
    33  		return convert(rawHTTPStatusErr)
    34  	}
    35  	return err
    36  }
    37  
    38  func convert(rawHTTPStatusErr ccerror.RawHTTPStatusError) error {
    39  	// Try to unmarshal the raw error into a CC error. If unmarshaling fails,
    40  	// return the raw error.
    41  	var errorResponse ccerror.V3ErrorResponse
    42  	err := json.Unmarshal(rawHTTPStatusErr.RawResponse, &errorResponse)
    43  
    44  	// error parsing json
    45  	if err != nil {
    46  		if rawHTTPStatusErr.StatusCode == http.StatusNotFound {
    47  			return ccerror.NotFoundError{Message: string(rawHTTPStatusErr.RawResponse)}
    48  		}
    49  		return rawHTTPStatusErr
    50  	}
    51  
    52  	errors := errorResponse.Errors
    53  	if len(errors) == 0 {
    54  		return ccerror.V3UnexpectedResponseError{
    55  			ResponseCode:    rawHTTPStatusErr.StatusCode,
    56  			V3ErrorResponse: errorResponse,
    57  		}
    58  	}
    59  
    60  	// There could be multiple errors in the future but for now we only convert
    61  	// the first error.
    62  	firstErr := errors[0]
    63  
    64  	switch rawHTTPStatusErr.StatusCode {
    65  	case http.StatusUnauthorized: // 401
    66  		if firstErr.Title == "CF-InvalidAuthToken" {
    67  			return ccerror.InvalidAuthTokenError{Message: firstErr.Detail}
    68  		}
    69  		return ccerror.UnauthorizedError{Message: firstErr.Detail}
    70  	case http.StatusForbidden: // 403
    71  		return ccerror.ForbiddenError{Message: firstErr.Detail}
    72  	case http.StatusNotFound: // 404
    73  		return handleNotFound(firstErr)
    74  	case http.StatusUnprocessableEntity: // 422
    75  		return handleUnprocessableEntity(firstErr)
    76  	case http.StatusServiceUnavailable: // 503
    77  		if firstErr.Title == "CF-TaskWorkersUnavailable" {
    78  			return ccerror.TaskWorkersUnavailableError{Message: firstErr.Detail}
    79  		}
    80  		return ccerror.ServiceUnavailableError{Message: firstErr.Detail}
    81  	default:
    82  		return ccerror.V3UnexpectedResponseError{
    83  			ResponseCode:    rawHTTPStatusErr.StatusCode,
    84  			RequestIDs:      rawHTTPStatusErr.RequestIDs,
    85  			V3ErrorResponse: errorResponse,
    86  		}
    87  	}
    88  }
    89  
    90  func handleNotFound(errorResponse ccerror.V3Error) error {
    91  	switch errorResponse.Detail {
    92  	case "App not found":
    93  		return ccerror.ApplicationNotFoundError{}
    94  	case "Droplet not found":
    95  		return ccerror.DropletNotFoundError{}
    96  	case "Instance not found":
    97  		return ccerror.InstanceNotFoundError{}
    98  	case "Process not found":
    99  		return ccerror.ProcessNotFoundError{}
   100  	default:
   101  		return ccerror.ResourceNotFoundError{Message: errorResponse.Detail}
   102  	}
   103  }
   104  
   105  func handleUnprocessableEntity(errorResponse ccerror.V3Error) error {
   106  	switch errorResponse.Detail {
   107  	case "name must be unique in space":
   108  		return ccerror.NameNotUniqueInSpaceError{}
   109  	case "Buildpack must be an existing admin buildpack or a valid git URI":
   110  		return ccerror.InvalidBuildpackError{}
   111  	default:
   112  		return ccerror.UnprocessableEntityError{Message: errorResponse.Detail}
   113  	}
   114  }