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 }