github.com/arunkumar7540/cli@v6.45.0+incompatible/api/cloudcontroller/ccv3/errors.go (about)

     1  package ccv3
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  	"strings"
     7  
     8  	"code.cloudfoundry.org/cli/api/cloudcontroller"
     9  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    10  )
    11  
    12  const taskWorkersUnavailable = "CF-TaskWorkersUnavailable"
    13  
    14  // errorWrapper is the wrapper that converts responses with 4xx and 5xx status
    15  // codes to an error.
    16  type errorWrapper struct {
    17  	connection cloudcontroller.Connection
    18  }
    19  
    20  func newErrorWrapper() *errorWrapper {
    21  	return new(errorWrapper)
    22  }
    23  
    24  // Make creates a connection in the wrapped connection and handles errors
    25  // that it returns.
    26  func (e *errorWrapper) Make(request *cloudcontroller.Request, passedResponse *cloudcontroller.Response) error {
    27  	err := e.connection.Make(request, passedResponse)
    28  
    29  	if rawHTTPStatusErr, ok := err.(ccerror.RawHTTPStatusError); ok {
    30  		if rawHTTPStatusErr.StatusCode >= http.StatusInternalServerError {
    31  			return convert500(rawHTTPStatusErr)
    32  		}
    33  		return convert400(rawHTTPStatusErr, request)
    34  	}
    35  	return err
    36  }
    37  
    38  // Wrap wraps a Cloud Controller connection in this error handling wrapper.
    39  func (e *errorWrapper) Wrap(innerconnection cloudcontroller.Connection) cloudcontroller.Connection {
    40  	e.connection = innerconnection
    41  	return e
    42  }
    43  
    44  func convert400(rawHTTPStatusErr ccerror.RawHTTPStatusError, request *cloudcontroller.Request) error {
    45  	firstErr, errorResponse, err := unmarshalFirstV3Error(rawHTTPStatusErr)
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	if len(errorResponse.Errors) > 1 {
    51  		return ccerror.MultiError{Errors: errorResponse.Errors, ResponseCode: rawHTTPStatusErr.StatusCode}
    52  	}
    53  
    54  	switch rawHTTPStatusErr.StatusCode {
    55  	case http.StatusUnauthorized: // 401
    56  		if firstErr.Title == "CF-InvalidAuthToken" {
    57  			return ccerror.InvalidAuthTokenError{Message: firstErr.Detail}
    58  		}
    59  		return ccerror.UnauthorizedError{Message: firstErr.Detail}
    60  	case http.StatusForbidden: // 403
    61  		return ccerror.ForbiddenError{Message: firstErr.Detail}
    62  	case http.StatusNotFound: // 404
    63  		return handleNotFound(firstErr, request)
    64  	case http.StatusUnprocessableEntity: // 422
    65  		return handleUnprocessableEntity(firstErr)
    66  	case http.StatusServiceUnavailable: // 503
    67  		if firstErr.Title == taskWorkersUnavailable {
    68  			return ccerror.TaskWorkersUnavailableError{Message: firstErr.Detail}
    69  		}
    70  		return ccerror.ServiceUnavailableError{Message: firstErr.Detail}
    71  	default:
    72  		return ccerror.V3UnexpectedResponseError{
    73  			ResponseCode:    rawHTTPStatusErr.StatusCode,
    74  			RequestIDs:      rawHTTPStatusErr.RequestIDs,
    75  			V3ErrorResponse: errorResponse,
    76  		}
    77  	}
    78  }
    79  
    80  func convert500(rawHTTPStatusErr ccerror.RawHTTPStatusError) error {
    81  	switch rawHTTPStatusErr.StatusCode {
    82  	case http.StatusServiceUnavailable: // 503
    83  		firstErr, _, err := unmarshalFirstV3Error(rawHTTPStatusErr)
    84  		if err != nil {
    85  			return err
    86  		}
    87  		if firstErr.Title == taskWorkersUnavailable {
    88  			return ccerror.TaskWorkersUnavailableError{Message: firstErr.Detail}
    89  		}
    90  		return ccerror.ServiceUnavailableError{Message: firstErr.Detail}
    91  	default:
    92  		return ccerror.V3UnexpectedResponseError{
    93  			ResponseCode: rawHTTPStatusErr.StatusCode,
    94  			RequestIDs:   rawHTTPStatusErr.RequestIDs,
    95  			V3ErrorResponse: ccerror.V3ErrorResponse{
    96  				Errors: []ccerror.V3Error{{
    97  					Detail: string(rawHTTPStatusErr.RawResponse),
    98  				}},
    99  			},
   100  		}
   101  	}
   102  }
   103  
   104  func handleNotFound(errorResponse ccerror.V3Error, request *cloudcontroller.Request) error {
   105  	switch errorResponse.Detail {
   106  	case "App not found":
   107  		return ccerror.ApplicationNotFoundError{}
   108  	case "Droplet not found":
   109  		return ccerror.DropletNotFoundError{}
   110  	case "Deployment not found":
   111  		return ccerror.DeploymentNotFoundError{}
   112  	case "Feature flag not found":
   113  		return ccerror.FeatureFlagNotFoundError{}
   114  	case "Instance not found":
   115  		return ccerror.InstanceNotFoundError{}
   116  	case "Process not found":
   117  		return ccerror.ProcessNotFoundError{}
   118  	case "Unknown request":
   119  		return ccerror.APINotFoundError{URL: request.URL.String()}
   120  	default:
   121  		return ccerror.ResourceNotFoundError{Message: errorResponse.Detail}
   122  	}
   123  }
   124  
   125  func handleUnprocessableEntity(errorResponse ccerror.V3Error) error {
   126  	//idea to make route already exist error flexible for all relevant error cases
   127  	errorString := errorResponse.Detail
   128  	err := ccerror.UnprocessableEntityError{Message: errorResponse.Detail}
   129  	// boolean switch case with partial/regex string matchers
   130  	switch {
   131  	case strings.Contains(errorString,
   132  		"name must be unique in space"):
   133  		return ccerror.NameNotUniqueInSpaceError{}
   134  	case strings.Contains(errorString,
   135  		"Route already exists"):
   136  		return ccerror.RouteNotUniqueError{UnprocessableEntityError: err}
   137  	case strings.Contains(errorString,
   138  		"Buildpack must be an existing admin buildpack or a valid git URI"):
   139  		return ccerror.InvalidBuildpackError{}
   140  	default:
   141  		return err
   142  	}
   143  }
   144  
   145  func unmarshalFirstV3Error(rawHTTPStatusErr ccerror.RawHTTPStatusError) (ccerror.V3Error, ccerror.V3ErrorResponse, error) {
   146  	// Try to unmarshal the raw error into a CC error. If unmarshaling fails,
   147  	// return the raw error.
   148  	var errorResponse ccerror.V3ErrorResponse
   149  	err := json.Unmarshal(rawHTTPStatusErr.RawResponse, &errorResponse)
   150  	if err != nil {
   151  		return ccerror.V3Error{}, errorResponse, ccerror.UnknownHTTPSourceError{
   152  			StatusCode:  rawHTTPStatusErr.StatusCode,
   153  			RawResponse: rawHTTPStatusErr.RawResponse,
   154  		}
   155  	}
   156  
   157  	errors := errorResponse.Errors
   158  	if len(errors) == 0 {
   159  		return ccerror.V3Error{}, errorResponse, ccerror.V3UnexpectedResponseError{
   160  			ResponseCode:    rawHTTPStatusErr.StatusCode,
   161  			V3ErrorResponse: errorResponse,
   162  		}
   163  	}
   164  
   165  	return errors[0], errorResponse, nil
   166  }