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}}