fmutils

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Mar 20, 2024 License: MIT Imports: 2 Imported by: 23

README

Golang protobuf FieldMask utils

Build Status Coverage Status PkgGoDev

Filter a protobuf message with a FieldMask applied
// Keeps the fields mentioned in the paths untouched, all the other fields will be cleared.
fmutils.Filter(protoMessage, []string{"a.b.c", "d"})
Prune a protobuf message with a FieldMask applied
// Clears all the fields mentioned in the paths, all the other fields will be left untouched.
fmutils.Prune(protoMessage, []string{"a.b.c", "d"})
Merge protobuf messages with a FieldMask applied
// Overwrites the fields in the dst from src.
// Only the fields listed in the field mask will be copied.
fmutils.Overwrite(src, dst, []string{"a.b.c", "d"})
Working with Golang protobuf APIv1

This library uses the new Go API for protocol buffers. If your *.pb.go files are generated with the old version APIv1 then you have 2 choices:

  • migrate to the new APIv2 google.golang.org/protobuf
  • upgrade an existing APIv1 version to github.com/golang/protobuf@v1.4.0 that implements the new API

In both cases you'll need to regenerate *.pb.go files.

If you decide to stay with APIv1 then you need to use the proto.MessageV2 function like this:

import protov1 "github.com/golang/protobuf/proto"

fmutils.Filter(protov1.MessageV2(protoMessage), []string{"a.b.c", "d"})

Read more about the Go protobuf API versions.

Examples

See the examples_test.go for real life examples.

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Filter

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

Filter keeps the msg fields that are listed in the paths and clears all the rest.

This is a handy wrapper for NestedMask.Filter method. If the same paths are used to process multiple proto messages use NestedMask.Filter method directly.

Example (Reuse_mask)

ExampleFilter_reuse_mask illustrates how a single NestedMask instance can be used to process multiple proto messages.

package main

import (
	"fmt"

	"github.com/mennanov/fmutils"
	"github.com/mennanov/fmutils/testproto"
)

func main() {
	users := []*testproto.User{
		{
			UserId: 1,
			Name:   "name 1",
		},
		{
			UserId: 2,
			Name:   "name 2",
		},
	}
	// Create a mask only once and reuse it.
	mask := fmutils.NestedMaskFromPaths([]string{"name"})
	for _, user := range users {
		mask.Filter(user)
	}
	fmt.Println(users)
}
Output:

[name:"name 1" name:"name 2"]
Example (Update_request)

ExampleFilter_update_request illustrates an API endpoint that updates an existing entity. The request to that endpoint provides a field mask that should be used to update the entity.

package main

import (
	"fmt"
	"regexp"

	"google.golang.org/genproto/protobuf/field_mask"
	"google.golang.org/protobuf/proto"

	"github.com/mennanov/fmutils"
	"github.com/mennanov/fmutils/testproto"
)

var reSpaces = regexp.MustCompile(`\s+`)

func main() {
	// Assuming the profile entity is loaded from a database.
	profile := &testproto.Profile{
		User: &testproto.User{
			UserId: 64,
			Name:   "user name",
		},
		Photo: &testproto.Photo{
			PhotoId: 2,
			Path:    "photo path",
			Dimensions: &testproto.Dimensions{
				Width:  100,
				Height: 120,
			},
		},
		LoginTimestamps: []int64{1, 2, 3},
	}
	// An API request from an API user.
	updateProfileRequest := &testproto.UpdateProfileRequest{
		Profile: &testproto.Profile{
			User: &testproto.User{
				UserId: 65, // not listed in the field mask, so won't be updated.
				Name:   "new user name",
			},
			Photo: &testproto.Photo{
				PhotoId: 3, // not listed in the field mask, so won't be updated.
				Path:    "new photo path",
				Dimensions: &testproto.Dimensions{
					Width: 50,
				},
			},
			LoginTimestamps: []int64{4, 5}},
		Fieldmask: &field_mask.FieldMask{
			Paths: []string{"user.name", "photo.path", "photo.dimensions.width", "login_timestamps"}},
	}
	// Normalize and validate the field mask before using it.
	updateProfileRequest.Fieldmask.Normalize()
	if !updateProfileRequest.Fieldmask.IsValid(profile) {
		// Return an error.
		panic("invalid field mask")
	}
	// Redact the request according to the provided field mask.
	fmutils.Filter(updateProfileRequest.GetProfile(), updateProfileRequest.Fieldmask.GetPaths())
	// Now that the request is vetted we can merge it with the profile entity.
	proto.Merge(profile, updateProfileRequest.GetProfile())
	// The profile can now be saved in a database.
	fmt.Println(reSpaces.ReplaceAllString(profile.String(), " "))
}
Output:

user:{user_id:64 name:"new user name"} photo:{photo_id:2 path:"new photo path" dimensions:{width:50 height:120}} login_timestamps:1 login_timestamps:2 login_timestamps:3 login_timestamps:4 login_timestamps:5

func Overwrite added in v0.2.1

func Overwrite(src, dest proto.Message, paths []string)

Overwrite overwrites all the fields listed in paths in the dest msg using values from src msg.

This is a handy wrapper for NestedMask.Overwrite method. If the same paths are used to process multiple proto messages use NestedMask.Overwrite method directly.

func Prune

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

Prune clears all the fields listed in paths from the given msg.

This is a handy wrapper for NestedMask.Prune method. If the same paths are used to process multiple proto messages use NestedMask.Filter method directly.

Types

type NestedMask

type NestedMask map[string]NestedMask

NestedMask represents a field mask as a recursive map.

func NestedMaskFromPaths

func NestedMaskFromPaths(paths []string) NestedMask

NestedMaskFromPaths creates an instance of NestedMask for the given paths.

func (NestedMask) Filter

func (mask NestedMask) Filter(msg proto.Message)

Filter keeps the msg fields that are listed in the paths and clears all the rest.

If the mask is empty then all the fields are kept. Paths are assumed to be valid and normalized otherwise the function may panic. See google.golang.org/protobuf/types/known/fieldmaskpb for details.

func (NestedMask) Overwrite added in v0.2.1

func (mask NestedMask) Overwrite(src, dest proto.Message)

Overwrite overwrites all the fields listed in paths in the dest msg using values from src msg.

All other fields are kept untouched. If the mask is empty, no fields are overwritten. Supports scalars, messages, repeated fields, and maps. If the parent of the field is nil message, the parent is initiated before overwriting the field If the field in src is empty value, the field in dest is cleared. Paths are assumed to be valid and normalized otherwise the function may panic.

func (NestedMask) Prune

func (mask NestedMask) Prune(msg proto.Message)

Prune clears all the fields listed in paths from the given msg.

All other fields are kept untouched. If the mask is empty no fields are cleared. This operation is the opposite of NestedMask.Filter. Paths are assumed to be valid and normalized otherwise the function may panic. See google.golang.org/protobuf/types/known/fieldmaskpb for details.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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