protofm

package module
v0.0.0-...-0125ad8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 13, 2021 License: Apache-2.0 Imports: 6 Imported by: 0

README

Proto field mask

Go Reference

Field masks allows gRPC consumers to select fields of interest to keep in the response. This reduces the amount of data being transferred from the server to the client.

This library extends the formal field mask definition by adding support for masking of fields inside repeated messages.

This library has the following features:

  • Field mask application with list support (protofm.Apply)
  • Field mask validation (protofm.Validate)
  • gRPC hook for convention-based application of field masks.

Background

A field mask is a list of strings with dot-separated field names from the response message.

Example

f {
  a : 22
  b {
    d : 1
    x : 2
  }
  y : 13
}
z: 8

Applying the field mask ["f.a", "f.b.d"] the above message gives:

f {
  a : 22
  b {
    d : 1
  }
}

If a field mask is missing or empty, no filtering takes place.

Applying a field mask

NOTE: it is strongly recommended to validate field masks before application to a Protobuf message. See protofm.Validate below.

There are two ways of applying a field mask on a proto message, either by sending both proto message and field mask paths to the ApplyMask function...

protofm.ApplyMask(pbMessage, []string{"f.a", "f.b.d"})

...or the preferred way is to first create a new instance of FieldMaskMap, validate it and then apply it on the proto message:

fm := protofm.NewMask([]string{"f.a", "f.b.d"})
if valid := fm.Validate(pbMessage); !valid {
    fmt.Println("the field mask was not valid for this message type")
}
fm.Apply(pbMessage)

Validating a field mask

protofm.Validate returns false if one or more of the paths in the field mask are not available in the proto message.

Validate is called either with proto message and field mask paths to the protofm.ValidateMask function...

if valid := protofm.ValidateMask(pbMessage, []string{"f.a", "f.b.d"}); !valid {
    fmt.Println("the field mask was not valid for this message type")
}

...or with an existing protofm.FieldMaskMap:

fm := protofm.NewMask([]string{"f.a", "f.b.d"})
if valid := fm.Validate(pbMessage); !valid {
    fmt.Println("the field mask was not valid for this message type")
}

gRPC hook

To automatically validate and apply field masks when .read_mask is present and non-empty on the request, add the following interceptors:

opts := []grpc.ServerOption{
    grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
        protofm.UnaryServerInterceptor(),
    )),
    grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
        protofm.StreamServerInterceptor(),
    )),
}

grpcServer = grpc.NewServer(opts...)

Under the hood, this checks whether the incoming request is ReadMaskable:

type ReadMaskable interface {
    GetReadMask() []string
}

Example proto definition that would trigger the interceptor:

message ExampleRequest {
    repeated string read_mask = 1;
    string some_other_field = 2;
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplyMask

func ApplyMask(msg proto.Message, paths []string)

ApplyMask keep the value of all given paths in the given proto message and clears the rest. Does not validate the field mask, this should be done before calling this function. A FieldMaskMap is created and then used for filtering the given proto message.

func StreamServerInterceptor

func StreamServerInterceptor() grpc.StreamServerInterceptor

StreamServerInterceptor will apply field masks on all streamed responses from Server streamed endpoints if the request * ... implement the ReadMaskable interface * ... the field mask paths are not empty * ... the field mask paths are valid on the response message

func UnaryServerInterceptor

func UnaryServerInterceptor() grpc.UnaryServerInterceptor

UnaryServerInterceptor will apply field masks on responses for unary endpoints if the request * ... implement the ReadMaskable interface * ... the field mask paths are not empty * ... the field mask paths are valid on the response message

func ValidateMask

func ValidateMask(msg proto.Message, paths []string) bool

ValidateMask validates that the paths are valid paths in the given protobuf message. A FieldMaskMap is created and then validated against the message.

Types

type FieldMaskMap

type FieldMaskMap map[string]FieldMaskMap

FieldMaskMap holds the information about which fields should be filtered in a protobuf message.

func NewMask

func NewMask(paths []string) FieldMaskMap

NewMask creates a FieldMaskMap from the paths provided. Path parts are separated by "." for example "base.sub.key".

func (FieldMaskMap) Apply

func (fm FieldMaskMap) Apply(msg proto.Message)

Apply the given proto message with the FieldMaskMap. Does not validate the field mask, this should be done before calling this function. Will keep all fields specified in the FieldMaskMap as is and clear the rest. If there are no paths in the FieldMaskMap all fields will be left as is.

func (FieldMaskMap) Validate

func (fm FieldMaskMap) Validate(msg proto.Message) bool

ValidateMask the FieldMaskMap against the given proto message.

type ReadMaskable

type ReadMaskable interface {
	GetReadMask() []string
}

ReadMaskable must be implemented by the requests for the interceptors to apply the field map on the responses.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL