forked from n8n-io/n8n
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request n8n-io#3 from crafted-systems/william-dev-branch
User module gRPC setup + others
- Loading branch information
Showing
29 changed files
with
2,056 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# GRPC | ||
|
||
## Usage | ||
|
||
Create a new default server with `grpc.NewServer`. Then run it with `grpc.StartServer`. | ||
|
||
## Caveats | ||
|
||
! Important note about usage of middlewares: | ||
When you chain middlewares with `grpc_middleware.ChainUnaryServer`, they will be chained such as `grpc_middleware.ChainUnaryServer(one, two, three)` will be performed in the following order: one, two, three, endpoint. This means that they will be wrapped in such a way that the final call will be equivalent to: | ||
|
||
```go | ||
three(two(one(handler()))) | ||
``` | ||
|
||
In case your middlewares are of the shape: | ||
|
||
```go | ||
func UnaryServerInterceptor(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { | ||
resp, err := handler(ctx, req) | ||
|
||
// Print the number of the middleware (eg. one, two, or three) | ||
|
||
return resp, err | ||
} | ||
``` | ||
|
||
then your output will look like: | ||
|
||
```plaintext | ||
three | ||
two | ||
one | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package errors | ||
|
||
import ( | ||
"google.golang.org/grpc/codes" | ||
"google.golang.org/grpc/status" | ||
) | ||
|
||
// GRPCError is just an error that can be unwrapped and contains a GRPC code. | ||
type GRPCError struct { | ||
Err error | ||
Code codes.Code | ||
} | ||
|
||
func (m GRPCError) Unwrap() error { | ||
return m.Err | ||
} | ||
|
||
func (m GRPCError) Error() string { | ||
return m.Err.Error() | ||
} | ||
|
||
func (m GRPCError) GRPCStatus() *status.Status { | ||
if m.Code == codes.OK && m.Err != nil { | ||
return status.New(codes.Unknown, m.Error()) | ||
} | ||
|
||
return status.New(m.Code, m.Error()) | ||
} | ||
|
||
func New(err error, code codes.Code) *GRPCError { | ||
return &GRPCError{ | ||
Err: err, | ||
Code: code, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package errors | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"google.golang.org/grpc/codes" | ||
"google.golang.org/grpc/status" | ||
) | ||
|
||
func TestGRPCErrorString(t *testing.T) { | ||
err := GRPCError{ | ||
Err: assert.AnError, | ||
Code: codes.Unknown, | ||
} | ||
|
||
assert.Error(t, err) | ||
assert.Contains(t, err.Error(), assert.AnError.Error()) | ||
} | ||
|
||
func TestGRPCErrorGRPC(t *testing.T) { | ||
err := GRPCError{ | ||
Err: assert.AnError, | ||
Code: codes.NotFound, | ||
} | ||
|
||
assert.Error(t, err) | ||
assert.Contains(t, err.Error(), assert.AnError.Error()) | ||
assert.Equal(t, codes.NotFound, status.Convert(err).Code()) | ||
} | ||
|
||
func TestGRPCErrorGRPCUnknown(t *testing.T) { | ||
err := GRPCError{ | ||
Err: assert.AnError, | ||
} | ||
|
||
assert.Error(t, err) | ||
assert.Contains(t, err.Error(), assert.AnError.Error()) | ||
assert.Equal(t, status.Convert(err).Code(), codes.Unknown) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package grpc | ||
|
||
import ( | ||
"net" | ||
|
||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" | ||
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery" | ||
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" | ||
"go.uber.org/zap" | ||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/reflection" | ||
) | ||
|
||
type ServerOpts struct { | ||
EnableReflection bool | ||
UnaryInterceptors []grpc.UnaryServerInterceptor | ||
ServerOptions []grpc.ServerOption | ||
} | ||
|
||
// NewServer creates a basic health server. | ||
func NewServer(opts ServerOpts) *grpc.Server { | ||
// create default interceptors | ||
var interceptors []grpc.UnaryServerInterceptor | ||
interceptors = append(interceptors, | ||
grpc_prometheus.UnaryServerInterceptor, | ||
grpc_recovery.UnaryServerInterceptor(), | ||
) | ||
interceptors = append(interceptors, opts.UnaryInterceptors...) | ||
|
||
serverOptions := []grpc.ServerOption{ | ||
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(interceptors...)), | ||
} | ||
|
||
serverOptions = append(serverOptions, opts.ServerOptions...) | ||
|
||
s := grpc.NewServer(serverOptions...) | ||
|
||
if opts.EnableReflection { | ||
reflection.Register(s) | ||
} | ||
|
||
grpc_prometheus.EnableHandlingTimeHistogram() | ||
grpc_prometheus.Register(s) | ||
|
||
return s | ||
} | ||
|
||
// StartServer starts a server. | ||
func StartServer(server *grpc.Server, port string) error { | ||
lis, err := net.Listen("tcp", port) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
zap.S().Infof("Successfully created gRPC server on %s. Attempting to serve.", port) | ||
|
||
return server.Serve(lis) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package zap | ||
|
||
import ( | ||
"context" | ||
|
||
grpc_logging "github.com/grpc-ecosystem/go-grpc-middleware/logging" | ||
grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" | ||
"go.uber.org/zap" | ||
"google.golang.org/grpc" | ||
) | ||
|
||
// WithIgnoreMethods is an option that prevents certain methods from logging. It can be used in AllowGRPCLogging. | ||
func WithIgnoreMethods(ignoredMethods []string) grpc_zap.Option { | ||
return grpc_zap.WithDecider(func(fullMethodName string, err error) bool { | ||
// will not log gRPC calls if it was a call to one of the ignoredMethods and no error was raised. | ||
if err == nil && in(fullMethodName, ignoredMethods) { | ||
return false | ||
} | ||
|
||
// by default everything will be logged | ||
return true | ||
}) | ||
} | ||
|
||
func in(target string, elements []string) bool { | ||
for _, e := range elements { | ||
if e == target { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
// AllowGRPCLogging allows a zap logger to be called from within grpc functions. It has to be added before you can add LogRequests. In the middleware chain, this means calling `grpc_middleware.ChainUnaryServer(AllowGRPCLogging, LogRequests)`. | ||
func AllowGRPCLogging(logger *zap.Logger, opts ...grpc_zap.Option) grpc.UnaryServerInterceptor { | ||
return grpc_zap.UnaryServerInterceptor(logger, opts...) | ||
} | ||
|
||
// WithLogRequestIgnoreMethods can be used for LogRequest to ignore certain methods. | ||
func WithLogRequestIgnoreMethods(ignoredMethods []string) grpc_logging.ServerPayloadLoggingDecider { | ||
return func(_ context.Context, fullMethodName string, _ interface{}) bool { | ||
// will not log gRPC calls if it was a call to one of the ignoredMethods. | ||
if in(fullMethodName, ignoredMethods) { //nolint:gosimple // in this case it's more clear this way. | ||
return false | ||
} | ||
|
||
// by default everything will be logged | ||
return true | ||
} | ||
} | ||
|
||
// LogRequests is just a wrapper for clarity of what it *actually* does. You need to add `AllowGRPCLogging` beforehand. In the middleware chain, this means calling `grpc_middleware.ChainUnaryServer(AllowGRPCLogging, LogRequests)`. | ||
func LogRequests(logger *zap.Logger, decider grpc_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor { | ||
return grpc_zap.PayloadUnaryServerInterceptor(logger, decider) | ||
} |
Oops, something went wrong.