github.com/vcilabs/webrpc@v0.5.2-0.20201116131534-162e27b1b33b/gen/golang/templates/helpers.go.tmpl (about) 1 {{define "helpers"}} 2 3 // 4 // Helpers 5 // 6 7 type ErrorPayload struct { 8 Status int `json:"status"` 9 Code string `json:"code"` 10 Cause string `json:"cause,omitempty"` 11 Msg string `json:"msg"` 12 Error string `json:"error"` 13 } 14 15 type Error interface { 16 // Code is of the valid error codes 17 Code() ErrorCode 18 19 // Msg returns a human-readable, unstructured messages describing the error 20 Msg() string 21 22 // Cause is reason for the error 23 Cause() error 24 25 // Error returns a string of the form "webrpc error <Code>: <Msg>" 26 Error() string 27 28 // Error response payload 29 Payload() ErrorPayload 30 } 31 32 func Errorf(code ErrorCode, msgf string, args ...interface{}) Error { 33 msg := fmt.Sprintf(msgf, args...) 34 if IsValidErrorCode(code) { 35 return &rpcErr{code: code, msg: msg} 36 } 37 return &rpcErr{code: ErrInternal, msg: "invalid error type " + string(code)} 38 } 39 40 func WrapError(code ErrorCode, cause error, format string, args ...interface{}) Error { 41 msg := fmt.Sprintf(format, args...) 42 if IsValidErrorCode(code) { 43 return &rpcErr{code: code, msg: msg, cause: cause} 44 } 45 return &rpcErr{code: ErrInternal, msg: "invalid error type " + string(code), cause: cause} 46 } 47 48 func Failf(format string, args ...interface{}) Error { 49 return Errorf(ErrFail, format, args...) 50 } 51 52 func WrapFailf(cause error, format string, args ...interface{}) Error { 53 return WrapError(ErrFail, cause, format, args...) 54 } 55 56 func ErrorNotFound(format string, args ...interface{}) Error { 57 return Errorf(ErrNotFound, format, args...) 58 } 59 60 func ErrorInvalidArgument(argument string, validationMsg string) Error { 61 return Errorf(ErrInvalidArgument, argument+" "+validationMsg) 62 } 63 64 func ErrorRequiredArgument(argument string) Error { 65 return ErrorInvalidArgument(argument, "is required") 66 } 67 68 func ErrorInternal(format string, args ...interface{}) Error { 69 return Errorf(ErrInternal, format, args...) 70 } 71 72 type ErrorCode string 73 74 const ( 75 // Unknown error. For example when handling errors raised by APIs that do not 76 // return enough error information. 77 ErrUnknown ErrorCode = "unknown" 78 79 // Fail error. General failure error type. 80 ErrFail ErrorCode = "fail" 81 82 // Canceled indicates the operation was cancelled (typically by the caller). 83 ErrCanceled ErrorCode = "canceled" 84 85 // InvalidArgument indicates client specified an invalid argument. It 86 // indicates arguments that are problematic regardless of the state of the 87 // system (i.e. a malformed file name, required argument, number out of range, 88 // etc.). 89 ErrInvalidArgument ErrorCode = "invalid argument" 90 91 // DeadlineExceeded means operation expired before completion. For operations 92 // that change the state of the system, this error may be returned even if the 93 // operation has completed successfully (timeout). 94 ErrDeadlineExceeded ErrorCode = "deadline exceeded" 95 96 // NotFound means some requested entity was not found. 97 ErrNotFound ErrorCode = "not found" 98 99 // BadRoute means that the requested URL path wasn't routable to a webrpc 100 // service and method. This is returned by the generated server, and usually 101 // shouldn't be returned by applications. Instead, applications should use 102 // NotFound or Unimplemented. 103 ErrBadRoute ErrorCode = "bad route" 104 105 // AlreadyExists means an attempt to create an entity failed because one 106 // already exists. 107 ErrAlreadyExists ErrorCode = "already exists" 108 109 // PermissionDenied indicates the caller does not have permission to execute 110 // the specified operation. It must not be used if the caller cannot be 111 // identified (Unauthenticated). 112 ErrPermissionDenied ErrorCode = "permission denied" 113 114 // Unauthenticated indicates the request does not have valid authentication 115 // credentials for the operation. 116 ErrUnauthenticated ErrorCode = "unauthenticated" 117 118 // ResourceExhausted indicates some resource has been exhausted, perhaps a 119 // per-user quota, or perhaps the entire file system is out of space. 120 ErrResourceExhausted ErrorCode = "resource exhausted" 121 122 // FailedPrecondition indicates operation was rejected because the system is 123 // not in a state required for the operation's execution. For example, doing 124 // an rmdir operation on a directory that is non-empty, or on a non-directory 125 // object, or when having conflicting read-modify-write on the same resource. 126 ErrFailedPrecondition ErrorCode = "failed precondition" 127 128 // Aborted indicates the operation was aborted, typically due to a concurrency 129 // issue like sequencer check failures, transaction aborts, etc. 130 ErrAborted ErrorCode = "aborted" 131 132 // OutOfRange means operation was attempted past the valid range. For example, 133 // seeking or reading past end of a paginated collection. 134 // 135 // Unlike InvalidArgument, this error indicates a problem that may be fixed if 136 // the system state changes (i.e. adding more items to the collection). 137 // 138 // There is a fair bit of overlap between FailedPrecondition and OutOfRange. 139 // We recommend using OutOfRange (the more specific error) when it applies so 140 // that callers who are iterating through a space can easily look for an 141 // OutOfRange error to detect when they are done. 142 ErrOutOfRange ErrorCode = "out of range" 143 144 // Unimplemented indicates operation is not implemented or not 145 // supported/enabled in this service. 146 ErrUnimplemented ErrorCode = "unimplemented" 147 148 // Internal errors. When some invariants expected by the underlying system 149 // have been broken. In other words, something bad happened in the library or 150 // backend service. Do not confuse with HTTP Internal Server Error; an 151 // Internal error could also happen on the client code, i.e. when parsing a 152 // server response. 153 ErrInternal ErrorCode = "internal" 154 155 // Unavailable indicates the service is currently unavailable. This is a most 156 // likely a transient condition and may be corrected by retrying with a 157 // backoff. 158 ErrUnavailable ErrorCode = "unavailable" 159 160 // DataLoss indicates unrecoverable data loss or corruption. 161 ErrDataLoss ErrorCode = "data loss" 162 163 // ErrNone is the zero-value, is considered an empty error and should not be 164 // used. 165 ErrNone ErrorCode = "" 166 ) 167 168 func HTTPStatusFromErrorCode(code ErrorCode) int { 169 switch code { 170 case ErrCanceled: 171 return 408 // RequestTimeout 172 case ErrUnknown: 173 return 400 // Bad Request 174 case ErrFail: 175 return 422 // Unprocessable Entity 176 case ErrInvalidArgument: 177 return 400 // BadRequest 178 case ErrDeadlineExceeded: 179 return 408 // RequestTimeout 180 case ErrNotFound: 181 return 404 // Not Found 182 case ErrBadRoute: 183 return 404 // Not Found 184 case ErrAlreadyExists: 185 return 409 // Conflict 186 case ErrPermissionDenied: 187 return 403 // Forbidden 188 case ErrUnauthenticated: 189 return 401 // Unauthorized 190 case ErrResourceExhausted: 191 return 403 // Forbidden 192 case ErrFailedPrecondition: 193 return 412 // Precondition Failed 194 case ErrAborted: 195 return 409 // Conflict 196 case ErrOutOfRange: 197 return 400 // Bad Request 198 case ErrUnimplemented: 199 return 501 // Not Implemented 200 case ErrInternal: 201 return 500 // Internal Server Error 202 case ErrUnavailable: 203 return 503 // Service Unavailable 204 case ErrDataLoss: 205 return 500 // Internal Server Error 206 case ErrNone: 207 return 200 // OK 208 default: 209 return 0 // Invalid! 210 } 211 } 212 213 func IsErrorCode(err error, code ErrorCode) bool { 214 if rpcErr, ok := err.(Error); ok { 215 if rpcErr.Code() == code { 216 return true 217 } 218 } 219 return false 220 } 221 222 func IsValidErrorCode(code ErrorCode) bool { 223 return HTTPStatusFromErrorCode(code) != 0 224 } 225 226 type rpcErr struct { 227 code ErrorCode 228 msg string 229 cause error 230 } 231 232 func (e *rpcErr) Code() ErrorCode { 233 return e.code 234 } 235 236 func (e *rpcErr) Msg() string { 237 return e.msg 238 } 239 240 func (e *rpcErr) Cause() error { 241 return e.cause 242 } 243 244 func (e *rpcErr) Error() string { 245 if e.cause != nil && e.cause.Error() != "" { 246 if e.msg != "" { 247 return fmt.Sprintf("webrpc %s error: %s -- %s", e.code, e.cause.Error(), e.msg) 248 } else { 249 return fmt.Sprintf("webrpc %s error: %s", e.code, e.cause.Error()) 250 } 251 } else { 252 return fmt.Sprintf("webrpc %s error: %s", e.code, e.msg) 253 } 254 } 255 256 func (e *rpcErr) Payload() ErrorPayload { 257 statusCode := HTTPStatusFromErrorCode(e.Code()) 258 errPayload := ErrorPayload{ 259 Status: statusCode, 260 Code: string(e.Code()), 261 Msg: e.Msg(), 262 Error: e.Error(), 263 } 264 if e.Cause() != nil { 265 errPayload.Cause = e.Cause().Error() 266 } 267 return errPayload 268 } 269 270 type contextKey struct { 271 name string 272 } 273 274 func (k *contextKey) String() string { 275 return "webrpc context value " + k.name 276 } 277 278 var ( 279 // For Client 280 HTTPClientRequestHeadersCtxKey = &contextKey{"HTTPClientRequestHeaders"} 281 282 // For Server 283 HTTPResponseWriterCtxKey = &contextKey{"HTTPResponseWriter"} 284 285 HTTPRequestCtxKey = &contextKey{"HTTPRequest"} 286 287 ServiceNameCtxKey = &contextKey{"ServiceName"} 288 289 MethodNameCtxKey = &contextKey{"MethodName"} 290 ) 291 292 {{end}}