Skip to content

Commit

Permalink
add README
Browse files Browse the repository at this point in the history
  • Loading branch information
linhbkhn95 committed Oct 1, 2024
1 parent b1c904f commit 4ad25b3
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 105 deletions.
77 changes: 77 additions & 0 deletions interceptors/fieldmask/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# fieldmask-middlware
Go grpc middleware for field mask
This package provides an interceptor that will `filter` server response by client intention.

# How to use
`Pre-condition`

You have to add `field_mask` into proto's request like
``` Protobuf
message Request{
// main fields
google.protobuf.FieldMask field_mask = 100;
}
```
1. install via go get

`go get github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/fieldmask`
`

2. Import and inject into grpc interceptor
The code in your application should be like that:
``` Go
import(
// ...
"google.golang.org/grpc"
fieldmaskpkg "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/fieldmask"


)
// ...

func main(){
var unaryOpts []grpc.UnaryServerInterceptor{
fieldmaskpkg.UnaryServerInterceptor(fieldmaskpkg.DefaultFilterFunc),
}
// Should append others interceptors
}
```
3. Client usage

`Protobuf definition`
```Protobuf
message GetProductRequest{
string id = 1;
google.protobuf.FieldMask field_mask = 2;
}
message Response{
message Result{
repeated Product products = 1;
}
Result result = 1;
}
message Product{
string id = 1;
string name = 2;
string img = 3;
decimal price = 4;
}
```

Client interaction

``` Go
func GetProduct(id string) *Product{
request := pb.GetProductRequest{
Id: "axaxaxx",
FieldMask: &fieldmaskpb.FieldMask{
Paths: []string{
"result.products.id", "result.products.name"
},
}
}
// ...
}
```
8 changes: 8 additions & 0 deletions interceptors/fieldmask/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) The go-grpc-middleware Authors.
// Licensed under the Apache License 2.0.

/*
Package fieldmask is a middleware that filter response base on client's request intent before return to clients.
Please see examples for simple examples of use.
*/
package fieldmask
24 changes: 24 additions & 0 deletions interceptors/fieldmask/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) The go-grpc-middleware Authors.
// Licensed under the Apache License 2.0.

package fieldmask

import (
"github.com/grpc-ecosystem/go-grpc-middleware/v2/testing/testpb"
"google.golang.org/grpc"
)

// Simple example of server initialization code.
func Example_serverConfig() {
_ = grpc.NewServer(
grpc.UnaryInterceptor(UnaryServerInterceptor(DefaultFilterFunc)),
)
}

// Simple example of server initialization code with fieldmask interceptor.
func Example_serverConfigWithAuthOverride() {
server := grpc.NewServer(
grpc.UnaryInterceptor(UnaryServerInterceptor(DefaultFilterFunc)),
)
testpb.RegisterTestServiceServer(server, &testpb.TestPingService{})
}
9 changes: 9 additions & 0 deletions interceptors/fieldmask/interceptor.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Copyright (c) The go-grpc-middleware Authors.
// Licensed under the Apache License 2.0.

package fieldmask

import (
Expand All @@ -16,6 +19,12 @@ var DefaultFilterFunc FilterFunc = func(msg proto.Message, paths []string) {
type FilterFunc func(msg proto.Message, paths []string)

// UnaryServerInterceptor returns a new unary server interceptor that will decide whether to which fields should return to clients.
//
// Example:
//
// var unaryOpts []grpc.UnaryServerInterceptor{
// fieldmaskpkg.UnaryServerInterceptor(fieldmaskpkg.DefaultFilterFunc),
// }
func UnaryServerInterceptor(filterFunc FilterFunc) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
resp, err = handler(ctx, req)
Expand Down
3 changes: 0 additions & 3 deletions interceptors/fieldmask/interceptor_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// Copyright (c) The go-grpc-middleware Authors.
// Licensed under the Apache License 2.0.

// Copyright 2017 David Ackroyd. All Rights Reserved.
// See LICENSE for licensing terms.

package fieldmask

import (
Expand Down
3 changes: 0 additions & 3 deletions interceptors/logging/interceptors.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,6 @@ func (c *reporter) PostMsgReceive(payload any, err error, duration time.Duration

func reportable(logger Logger, opts *options) interceptors.CommonReportableFunc {
return func(ctx context.Context, c interceptors.CallMeta) (interceptors.Reporter, context.Context) {
if !opts.shouldReport(c.FullMethod()) {
return interceptors.NoopReporter{}, ctx
}
kind := KindServerFieldValue
if c.IsClient {
kind = KindClientFieldValue
Expand Down
99 changes: 0 additions & 99 deletions interceptors/logging/interceptors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,6 @@ import (
"google.golang.org/protobuf/proto"
)

const (
// TODO: should import from testpb when we upgrade proto generator.
// It will look like Health service:
// https://github.com/grpc/grpc-go/blob/master/health/grpc_health_v1/health_grpc.pb.go#L39
TestPingFullMethodName = "/testing.testpb.v1.TestService/Ping"
)

type testDisposableFields map[string]string

func (f testDisposableFields) AssertField(t *testing.T, key, value string) testDisposableFields {
Expand Down Expand Up @@ -805,87 +798,6 @@ func (s *loggingCustomGrpcLogFieldsSuite) TestCustomGrpcLogFieldsWithPingList()
AssertField(s.T(), logging.ServiceFieldKey, testpb.TestServiceFullName).AssertNoMoreTags(s.T())
}

type loggingWithDeciderSuite struct {
*baseLoggingSuite
}

func TestWithDeciderSuite(t *testing.T) {
s := &loggingWithDeciderSuite{
baseLoggingSuite: &baseLoggingSuite{
logger: newMockLogger(),
InterceptorTestSuite: &testpb.InterceptorTestSuite{
TestService: &testpb.TestPingService{},
},
},
}
fullMethodNamesWithoutLogging := []string{TestPingFullMethodName}
s.InterceptorTestSuite.ClientOpts = []grpc.DialOption{
grpc.WithUnaryInterceptor(logging.UnaryClientInterceptor(s.logger, logging.WithDecider(ignoreLoggingDecider(fullMethodNamesWithoutLogging)))),
grpc.WithStreamInterceptor(logging.StreamClientInterceptor(s.logger, logging.WithDecider(ignoreLoggingDecider(fullMethodNamesWithoutLogging)))),
}
s.InterceptorTestSuite.ServerOpts = []grpc.ServerOption{
grpc.StreamInterceptor(logging.StreamServerInterceptor(s.logger, logging.WithDecider(ignoreLoggingDecider(fullMethodNamesWithoutLogging)))),
grpc.UnaryInterceptor(logging.UnaryServerInterceptor(s.logger, logging.WithDecider(ignoreLoggingDecider(fullMethodNamesWithoutLogging)))),
}
suite.Run(t, s)
}

// Test that do not have logs with using withDecider. Because this method is not allowed to report logs.
func (s *loggingWithDeciderSuite) TestDeciderWithPing() {
_, err := s.Client.Ping(s.SimpleCtx(), testpb.GoodPing)
assert.NoError(s.T(), err, "there must be not be an on a successful call")

lines := s.logger.o.Lines()
sort.Sort(lines)
require.Len(s.T(), lines, 0)
}

// Test that have logs with using withDecider. Because this method is allowed to report logs.
func (s *loggingWithDeciderSuite) TestDeciderWithPingList() {
stream, err := s.Client.PingList(s.SimpleCtx(), testpb.GoodPingList)
assert.NoError(s.T(), err, "there must be not be an on a successful call")

require.NoError(s.T(), err, "should not fail on establishing the stream")
for {
_, err := stream.Recv()
if err == io.EOF {
break
}
require.NoError(s.T(), err, "reading stream should not fail")
}

lines := s.logger.o.Lines()
sort.Sort(lines)
require.Len(s.T(), lines, 4)

serverStartedCallLogLine := lines[3]
assert.Equal(s.T(), logging.LevelInfo, serverStartedCallLogLine.lvl)
assert.Equal(s.T(), "started call", serverStartedCallLogLine.msg)
_ = assertStandardFields(s.T(), logging.KindServerFieldValue, serverStartedCallLogLine.fields, "PingList", interceptors.ServerStream)

clientStartedCallLogLine := lines[1]
assert.Equal(s.T(), logging.LevelDebug, clientStartedCallLogLine.lvl)
assert.Equal(s.T(), "started call", clientStartedCallLogLine.msg)
_ = assertStandardFields(s.T(), logging.KindClientFieldValue, clientStartedCallLogLine.fields, "PingList", interceptors.ServerStream)

serverFinishCallLogLine := lines[2]
assert.Equal(s.T(), logging.LevelInfo, serverFinishCallLogLine.lvl)
assert.Equal(s.T(), "finished call", serverFinishCallLogLine.msg)
serverFinishCallFields := assertStandardFields(s.T(), logging.KindServerFieldValue, serverFinishCallLogLine.fields, "PingList", interceptors.ServerStream)
serverFinishCallFields.AssertFieldNotEmpty(s.T(), "peer.address").
AssertFieldNotEmpty(s.T(), "grpc.start_time").
AssertFieldNotEmpty(s.T(), "grpc.request.deadline").
AssertField(s.T(), "grpc.code", "OK")

clientFinishCallLogLine := lines[0]
assert.Equal(s.T(), logging.LevelDebug, clientFinishCallLogLine.lvl)
assert.Equal(s.T(), "finished call", clientFinishCallLogLine.msg)
clientFinishCallFields := assertStandardFields(s.T(), logging.KindClientFieldValue, clientFinishCallLogLine.fields, "PingList", interceptors.ServerStream)
clientFinishCallFields.AssertFieldNotEmpty(s.T(), "grpc.start_time").
AssertFieldNotEmpty(s.T(), "grpc.request.deadline").
AssertField(s.T(), "grpc.code", "OK")
}

// waitUntil executes f every interval seconds until timeout or no error is returned from f.
func waitUntil(interval time.Duration, stopc <-chan struct{}, f func() error) error {
tick := time.NewTicker(interval)
Expand All @@ -903,14 +815,3 @@ func waitUntil(interval time.Duration, stopc <-chan struct{}, f func() error) er
}
}
}

func ignoreLoggingDecider(fullMethodNames []string) logging.Decider {
return func(fullMethodName string) bool {
for _, f := range fullMethodNames {
if fullMethodName == f {
return false
}
}
return true
}
}

0 comments on commit 4ad25b3

Please sign in to comment.