Skip to content

Commit

Permalink
Merge pull request #162 from nitrictech/feature/grpc-errors
Browse files Browse the repository at this point in the history
feat: add grpc errors
  • Loading branch information
medgar-nitric authored Sep 14, 2021
2 parents 1f4c3d5 + dde5308 commit 51d5188
Show file tree
Hide file tree
Showing 29 changed files with 485 additions and 215 deletions.
32 changes: 32 additions & 0 deletions contracts/proto/error/v1/error.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
syntax = "proto3";
package nitric.error.v1;

// protoc plugin options for code generation
option go_package = "nitric/v1;v1";
option java_package = "io.nitric.proto.error.v1";
option java_multiple_files = true;
option java_outer_classname = "Errors";
option php_namespace = "Nitric\\Proto\\Error\\V1";
option csharp_namespace = "Nitric.Proto.Error.v1";

message ErrorScope {
// The API service invoked, e.g. 'Service.Method'.
string service = 1;

// The plugin method invoked, e.g. 'PluginService.Method'.
string plugin = 2;

// The plugin method arguments, ensure only non-sensitive data is specified.
map<string, string> args = 3;
}

message ErrorDetails {
// The developer error message, explaining the error and ideally solution.
string message = 1;

// The error root cause.
string cause = 2;

// The scope of the error.
ErrorScope scope = 3;
}
51 changes: 1 addition & 50 deletions go.sum

Large diffs are not rendered by default.

102 changes: 99 additions & 3 deletions pkg/adapters/grpc/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
package grpc

import (
"fmt"
"reflect"

v1 "github.com/nitric-dev/membrane/interfaces/nitric/v1"
"github.com/nitric-dev/membrane/pkg/plugins/errors"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand All @@ -23,17 +27,109 @@ import (
// Provides GRPC error reporting
func NewGrpcError(operation string, err error) error {
if pe, ok := err.(*errors.PluginError); ok {
return newGrpcErrorWithCode(codes.Code(errors.Code(pe)), operation, pe)
code := codes.Code(errors.Code(pe))

ed := &v1.ErrorDetails{}
ed.Message = pe.Msg
if pe.Cause != nil {
ed.Cause = pe.Cause.Error()
}
ed.Scope = &v1.ErrorScope{
Service: operation,
Plugin: pe.Plugin,
}
if len(pe.Args) > 0 {
args := make(map[string]string)
for k, v := range pe.Args {
args[k] = LogArg(v)
}
ed.Scope.Args = args
}

s := status.New(code, pe.Msg)
s, _ = s.WithDetails(ed)

return s.Err()

} else {
return newGrpcErrorWithCode(codes.Internal, operation, err)
}
}

func newGrpcErrorWithCode(code codes.Code, operation string, err error) error {
return status.Errorf(code, "%s: %v", operation, err)
ed := &v1.ErrorDetails{}
ed.Message = err.Error()
ed.Scope = &v1.ErrorScope{
Service: operation,
}

s := status.New(code, err.Error())
s, _ = s.WithDetails(ed)

return s.Err()
}

// Provides generic error for unregistered plugins
func NewPluginNotRegisteredError(plugin string) error {
return status.Errorf(codes.Unimplemented, "%s plugin not registered", plugin)
ed := &v1.ErrorDetails{}
ed.Message = fmt.Sprintf("%s plugin not registered", plugin)

s := status.New(codes.Unimplemented, ed.Message)
s, _ = s.WithDetails(ed)

return s.Err()
}

func LogArg(arg interface{}) string {
value := getValue(arg)

if value.Kind() == reflect.Struct {

str := "{"
for i := 0; i < value.NumField(); i++ {

fieldType := value.Type().Field(i)
tag := fieldType.Tag.Get("log")
if tag == "" || tag == "-" {
continue
}

if len(str) > 1 {
str += ", "
}

field := value.Field(i)
str += fieldType.Name + ": " + LogArg(field.Interface())
}
str += "}"

return str

} else if value.Kind() == reflect.Map {
str := "{"

for k, v := range arg.(map[string]interface{}) {
if len(str) > 1 {
str += ", "
}
str += fmt.Sprintf("%v", k) + ": " + LogArg(v)
}

str += "}"

return str

} else {
return fmt.Sprintf("%v", arg)
}
}

func getValue(x interface{}) reflect.Value {
val := reflect.ValueOf(x)

if val.Kind() == reflect.Ptr {
val = val.Elem()
}

return val
}
97 changes: 94 additions & 3 deletions pkg/adapters/grpc/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,50 @@ import (
. "github.com/onsi/gomega"
)

type SecretValue struct {
Type string `log:"Type"`
Factor int `log:"-"`
Value string
}

type Secret struct {
Name string `json:"Name" log:"Name"`
Version string `json:"Version" log:"Version"`
Value *SecretValue `json:"Value" log:"Value"`
}

var _ = Describe("GRPC Errors", func() {
Context("GrpcError", func() {
When("plugin.errors.InvalidArgument", func() {
It("Should report GRPC IllegalArgument error", func() {
newErr := errors.ErrorsWithScope("test")
newErr := errors.ErrorsWithScope("test", nil)
err := newErr(
codes.InvalidArgument,
"bad param",
nil,
)
grpcErr := grpc.NewGrpcError("BadServer.BadCall", err)
Expect(grpcErr.Error()).To(ContainSubstring("rpc error: code = InvalidArgument desc = bad param"))
})
})
When("plugin.errors.InvalidArgument args", func() {
It("Should report GRPC IllegalArgument error with args", func() {
args := map[string]interface{}{"key": "value"}
newErr := errors.ErrorsWithScope("test", args)
err := newErr(
codes.InvalidArgument,
"bad param",
nil,
)
grpcErr := grpc.NewGrpcError("BadServer.BadCall", err)
Expect(grpcErr.Error()).To(ContainSubstring("rpc error: code = InvalidArgument desc = BadServer.BadCall: test([]): bad param"))
Expect(grpcErr.Error()).To(ContainSubstring("rpc error: code = InvalidArgument desc = bad param"))
})
})
When("Standard Error", func() {
It("Should report GRPC Internal error", func() {
err := fmt.Errorf("internal error")
err = grpc.NewGrpcError("BadServer.BadCall", err)
Expect(err.Error()).To(ContainSubstring("rpc error: code = Internal desc = BadServer.BadCall: internal error"))
Expect(err.Error()).To(ContainSubstring("rpc error: code = Internal desc = internal error"))
})
})
})
Expand All @@ -55,4 +80,70 @@ var _ = Describe("GRPC Errors", func() {
})
})
})

Context("Logging Arg", func() {
When("string", func() {
It("return string value", func() {
Expect(grpc.LogArg("string")).To(BeEquivalentTo("string"))
})
})

When("int", func() {
It("return string value", func() {
Expect(grpc.LogArg(123)).To(BeEquivalentTo("123"))
})
})

When("bool", func() {
It("return string value", func() {
Expect(grpc.LogArg(true)).To(BeEquivalentTo("true"))
})
})

When("float", func() {
It("return string value", func() {
Expect(grpc.LogArg(3.1415)).To(BeEquivalentTo("3.1415"))
})
})

When("struct", func() {
It("return string value", func() {

data := Secret{
Name: "name",
Version: "3",
Value: &SecretValue{
Type: "key",
Factor: 2,
Value: "2a4wijgPq0PpwJ76IjT7&lTBZ$5SGRcq",
},
}

value := grpc.LogArg(data)
Expect(value).To(BeEquivalentTo("{Name: name, Version: 3, Value: {Type: key}}"))
})
})

When("map", func() {
It("return string value", func() {
secret := Secret{
Name: "name",
Version: "3",
Value: &SecretValue{
Type: "key",
Factor: 2,
Value: "2a4wijgPq0PpwJ76IjT7&lTBZ$5SGRcq",
},
}

valueMap := map[string]interface{}{
"key": "value",
"secret": secret,
}
value := grpc.LogArg(valueMap)
Expect(value).To(ContainSubstring("secret: {Name: name, Version: 3, Value: {Type: key}}"))
Expect(value).To(ContainSubstring("key: value"))
})
})
})
})
2 changes: 0 additions & 2 deletions pkg/adapters/grpc/event_grpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package grpc_test

import (
"context"
"fmt"

"github.com/nitric-dev/membrane/pkg/adapters/grpc"

Expand All @@ -36,7 +35,6 @@ type MockEventService struct {
}

func (m *MockEventService) Publish(topic string, event *events.NitricEvent) error {
fmt.Printf("Publish called %v", event)
m.PublishTopic = topic
m.PublishEvent = event
return m.PublishError
Expand Down
16 changes: 12 additions & 4 deletions pkg/plugins/document/boltdb/boltdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ func (d BoltDoc) String() string {
func (s *BoltDocService) Get(key *document.Key) (*document.Document, error) {
newErr := errors.ErrorsWithScope(
"BoltDocService.Get",
fmt.Sprintf("key=%v", key),
map[string]interface{}{
"key": key,
},
)

if err := document.ValidateKey(key); err != nil {
Expand Down Expand Up @@ -105,7 +107,9 @@ func (s *BoltDocService) Get(key *document.Key) (*document.Document, error) {
func (s *BoltDocService) Set(key *document.Key, content map[string]interface{}) error {
newErr := errors.ErrorsWithScope(
"BoltDocService.Set",
fmt.Sprintf("key=%v", key),
map[string]interface{}{
"key": key,
},
)

if err := document.ValidateKey(key); err != nil {
Expand Down Expand Up @@ -151,7 +155,9 @@ func (s *BoltDocService) Set(key *document.Key, content map[string]interface{})
func (s *BoltDocService) Delete(key *document.Key) error {
newErr := errors.ErrorsWithScope(
"BoltDocService.Delete",
fmt.Sprintf("key=%v", key),
map[string]interface{}{
"key": key,
},
)

if err := document.ValidateKey(key); err != nil {
Expand Down Expand Up @@ -212,7 +218,9 @@ func (s *BoltDocService) Delete(key *document.Key) error {
func (s *BoltDocService) Query(collection *document.Collection, expressions []document.QueryExpression, limit int, pagingToken map[string]string) (*document.QueryResult, error) {
newErr := errors.ErrorsWithScope(
"BoltDocService.Query",
fmt.Sprintf("collection=%v", collection),
map[string]interface{}{
"collection": collection,
},
)

if err := document.ValidateQueryCollection(collection); err != nil {
Expand Down
16 changes: 12 additions & 4 deletions pkg/plugins/document/dynamodb/dynamodb.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ type DynamoDocService struct {
func (s *DynamoDocService) Get(key *document.Key) (*document.Document, error) {
newErr := errors.ErrorsWithScope(
"DynamoDocService.Get",
fmt.Sprintf("key=%v", key),
map[string]interface{}{
"key": key,
},
)

err := document.ValidateKey(key)
Expand Down Expand Up @@ -119,7 +121,9 @@ func (s *DynamoDocService) Get(key *document.Key) (*document.Document, error) {
func (s *DynamoDocService) Set(key *document.Key, value map[string]interface{}) error {
newErr := errors.ErrorsWithScope(
"DynamoDocService.Set",
fmt.Sprintf("key=%v", key),
map[string]interface{}{
"key": key,
},
)

if err := document.ValidateKey(key); err != nil {
Expand Down Expand Up @@ -175,7 +179,9 @@ func (s *DynamoDocService) Set(key *document.Key, value map[string]interface{})
func (s *DynamoDocService) Delete(key *document.Key) error {
newErr := errors.ErrorsWithScope(
"DynamoDocService.Delete",
fmt.Sprintf("key=%v", key),
map[string]interface{}{
"key": key,
},
)

if err := document.ValidateKey(key); err != nil {
Expand Down Expand Up @@ -258,7 +264,9 @@ func (s *DynamoDocService) Delete(key *document.Key) error {
func (s *DynamoDocService) Query(collection *document.Collection, expressions []document.QueryExpression, limit int, pagingToken map[string]string) (*document.QueryResult, error) {
newErr := errors.ErrorsWithScope(
"DynamoDocService.Query",
fmt.Sprintf("collection=%v", collection),
map[string]interface{}{
"collection": collection,
},
)

if err := document.ValidateQueryCollection(collection); err != nil {
Expand Down
Loading

0 comments on commit 51d5188

Please sign in to comment.