drone/gitrpc/internal/middleware/error.go
2023-03-06 16:36:19 +01:00

94 lines
2.2 KiB
Go

// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by the Polyform Free Trial License
// that can be found in the LICENSE.md file for this repository.
package middleware
import (
"context"
"errors"
"reflect"
"github.com/rs/zerolog/log"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type ErrInterceptor struct {
}
func NewErrInterceptor() ErrInterceptor {
return ErrInterceptor{}
}
func (i ErrInterceptor) UnaryInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
value, err := handler(ctx, req)
if (value == nil || reflect.ValueOf(value).IsNil()) && err == nil {
return nil, status.Error(codes.Internal, "service returned no error and no object")
}
err = processError(ctx, err)
return value, err
}
}
func (i ErrInterceptor) StreamInterceptor() grpc.StreamServerInterceptor {
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo,
handler grpc.StreamHandler) error {
err := handler(srv, stream)
err = processError(stream.Context(), err)
return err
}
}
func processError(ctx context.Context, err error) (rerr error) {
if err == nil {
return nil
}
defer func() {
statusErr, ok := status.FromError(rerr)
if !ok {
return
}
//nolint: exhaustive // log only server side errors, no need to log user based errors
switch statusErr.Code() {
case codes.Unknown,
codes.DeadlineExceeded,
codes.ResourceExhausted,
codes.FailedPrecondition,
codes.Aborted,
codes.OutOfRange,
codes.Unimplemented,
codes.Internal,
codes.Unavailable,
codes.DataLoss:
{
logCtx := log.Ctx(ctx)
logCtx.Error().Msg(err.Error())
}
}
}()
// custom errors should implement StatusError
var statusError interface {
Status() (*status.Status, error)
}
if errors.As(err, &statusError) {
st, sterr := statusError.Status()
if sterr != nil {
return sterr
}
return st.Err()
}
if status, ok := status.FromError(err); ok {
return status.Err()
}
return status.Errorf(codes.Unknown, err.Error())
}