Skip to content

Latest commit

 

History

History
225 lines (164 loc) · 5.24 KB

README.md

File metadata and controls

225 lines (164 loc) · 5.24 KB

gRPC helpers, middlewares ... for Golang

Package visigrpc provide some functions to help you making a gRPC server. Errors logged on Sentry, Panic interceptor, HTTP status to gRPC code, ... Everything is made to assist you :)

Table of contents

Install

Use go get to install this package.

go get github.com/visiperf/visigrpc

Usage

Status

Error

The Error(code codes.Code, msg string) error function is used to return a gRPC error and log it into Sentry.

type server struct { }

func main() {
  // init Sentry config
  if err := sentry.Init(sentry.ClientOptions{
    Dsn:         SENTRY_DSN,
    Environment: SENTRY_ENV,
    Transport:   sentry.NewHTTPSyncTransport(),
  }); err != nil {
    ...
  }
  
  // gRPC server
  lis, err := net.Listen("tcp", ":9090")
  if err != nil {
    ...
  }
  
  s := grpc.NewServer()
  
  RegisterServiceServer(s, &server{})
  
  if err := s.Serve(lis); err != nil {
    ...
  }
}

func (s *server) Get(ctx context.Context, in *GetRequest) (*GetResponse, error) {
  return nil, status.Error(codes.Unimplemented, "implement me")
}
IMPORTANT : Only Unknown, Internal and DataLoss errors will be reported in Sentry !

New

The New(code codes.Code, msg string) *spb.Status function has same process as Error(...) error function, but returns a *spb.Status instance instead of error.

type server struct { }

func main() {
  // init Sentry config
  if err := sentry.Init(sentry.ClientOptions{
    Dsn:         SENTRY_DSN,
    Environment: SENTRY_ENV,
    Transport:   sentry.NewHTTPSyncTransport(),
  }); err != nil {
    ...
  }
  
  // gRPC server
  lis, err := net.Listen("tcp", ":9090")
  if err != nil {
    ...
  }
  
  s := grpc.NewServer()
  
  RegisterServiceServer(s, &server{})
  
  if err := s.Serve(lis); err != nil {
    ...
  }
}

func (s *server) Get(ctx context.Context, in *GetRequest) (*GetResponse, error) {
  st := status.New(codes.Internal, "Oups, an error !")
  // st.Code -> codes.Internal
  // st.Message -> "Oups, an error !"
  ...
  
  return nil, status.Error(codes.Unimplemented, "implement me")
}

From Error

If you receive a gRPC error (made with status.Error(...)), you can decode it with FromError(err error) *spb.Status to retrieve the gRPC code and message.

type server struct { }

func main() {
  // init Sentry config
  if err := sentry.Init(sentry.ClientOptions{
    Dsn:         SENTRY_DSN,
    Environment: SENTRY_ENV,
    Transport:   sentry.NewHTTPSyncTransport(),
  }); err != nil {
    ...
  }
  
  // gRPC server
  lis, err := net.Listen("tcp", ":9090")
  if err != nil {
    ...
  }
  
  s := grpc.NewServer()
  
  RegisterServiceServer(s, &server{})
  
  if err := s.Serve(lis); err != nil {
    ...
  }
}

func (s *server) Get(ctx context.Context, in *GetRequest) (*GetResponse, error) {
  return nil, status.Error(codes.Unimplemented, "implement me")
}

func (s *server) Create(ctx context.Context, in *CreateRequest) (*CreateResponse, error) {
  resp, err := s.Get(ctx, &GetRequest{}) // just for example, never directly call self functions with `s *server` !
  if err != nil {
    st := status.FromError(err)
    // st.Code -> codes.Unimplemented
    // st.Message -> "implement me"
    ...
  }
  
  return nil, status.Error(codes.Unimplemented, "implement me")
}

gRPC code from HTTP status

If you make an HTTP request, you can use the GrpcCodeFromHttpStatus(status int) codes.Code func to convert HTTP status code in response to gRPC code.

code := status.GrpcCodeFromHttpStatus(http.StatusForbidden) // http status -> 403 (Forbidden)

// code -> 7 (codes.PermissionDenied)

Panic interceptor

Visigrpc also provide a RecoveryHandler to capture and log panics for Unary and Stream functions.

type server struct { }

func main() {
  // init Sentry config
  if err := sentry.Init(sentry.ClientOptions{
    Dsn:         SENTRY_DSN,
    Environment: SENTRY_ENV,
    Transport:   sentry.NewHTTPSyncTransport(),
  }); err != nil {
    ...
  }
  
  // gRPC server
  lis, err := net.Listen("tcp", ":9090")
  if err != nil {
    ...
  }
  
  var opts = []grpc_recovery.Option{
    grpc_recovery.WithRecoveryHandler(visigrpc.RecoveryHandler),
  }

  s := grpc.NewServer(
    grpc_middleware.WithUnaryServerChain(
       grpc_recovery.UnaryServerInterceptor(opts...),
    ),
    grpc_middleware.WithStreamServerChain(
       grpc_recovery.StreamServerInterceptor(opts...),
    ),
  )
  
  RegisterServiceServer(s, &server{})
  
  if err := s.Serve(lis); err != nil {
    ...
  }
}

func (s *server) Get(ctx context.Context, in *GetRequest) (*GetResponse, error) {
  panic("implement me") // will return codes.Unknown with message "implement me" and log error on Sentry
}

References