yeqown / protoc-gen-fieldmask

Generate FieldMask utility functions for protobuf
MIT License
34 stars 9 forks source link
fieldmask go grpc plugin protobuf protoc protoc-gen-fieldmask protocol-buffers

protoc-gen-fieldmask

GitHub release (latest SemVer) GitHub go.mod Go version License

Generate FieldMask utilities for protobuf, support Go, maybe more programing languages later. FieldMask is a protobuf message type, it's used to represent a set of fields those should be contained in response, and sent to Client. It looks like grapgql but only takes effect on the server inside calls.

To help developer to avoid repeating codes to deal with FieldMask message, this plugin generates a set of utilities to deal with FieldMask message.

1. Masking gRPC response case


2. Incremental update case

Features

Installation

go install github.com/yeqown/protoc-gen-fieldmask@latest

Examples

Usage guide

protoc \
    -I. \
    -I$YOUR_PROTO_PATH \
    --go_out=paths=source_relative:. \
    --fieldmask_out=paths=source_relative,lang=go:. \
    example.proto

Generated Preview

  1. coding proto file user.proto
syntax = "proto3";

import "google/protobuf/field_mask.proto";

message UserInfoRequest {
  string user_id = 1;
  google.protobuf.FieldMask field_mask = 2 [
    // generate MaskIn_XXX and MaskedIn_XXX, xxx are fields in in message (UserInfoRequest). 
    (fieldmask.option.Option).in = {gen: true},
    // generate MaskOut_XXX and MaskedOut_XXX, xxx are fields in out message (UserInfoResponse).
    // Notice that: 
    // 1. you must set message to out message name, and in and out message should in a same proto file.
    // 2. if out message is not specified, it will not generated correctly. 
    (fieldmask.option.Option).out = {gen: true, message:"UserInfoResponse"}
  ];
}

message Address {
  string country = 1;
  string province = 2;
}

message UserInfoResponse {
  string user_id = 1;
  string name = 2;
  string email = 3;
  Address address = 4;
}
  1. generated user.pb.go, user.pb.fm.go
cd examples && make gen-pb

# or generate them manually
cd examples
protoc \
        -I./pb \
        -I../proto \
        --go_out=paths=source_relative:./pb \
        --fieldmask_out=paths=source_relative,lang=go:./pb \
        ./pb/user.proto
  1. sample usage codes, on the one hand, to minimize changes to existing code, you just omit fields by field mask like this:
func main() {
  req := &normal.UserInfoRequest{
    UserId:    "123123",
    FieldMask: nil,
  }

  // enable field mask on specific fields.
  req.MaskOut_Email()
  req.MaskOut_Name()

  filter := req.FieldMask_Filter()
  // or use prune mode, so clear fields those are masked. 
  // prune := req.FieldMask_Prune()

  resp := &normal.UserInfoResponse{
    UserId: "69781",
    Name:   "yeqown",
    Email:  "yeqown@gmail.com",
    Address: &normal.Address{
      Country:  "China",
      Province: "Sichuan",
    },
  }

  // makes filter or prune effect on resp.
  prune.Mask(resp)
}

on the other hand, you can take FieldMask effect before the resp has been filled, so that you can ignore unnecessary calculating or remote calls:

func main() {
  req := &normal.UserInfoRequest{
    UserId:    "123123",
    FieldMask: nil,
  }

  // enable field mask on specific fields.
  req.MaskOut_Email()
  req.MaskOut_Name()

  filter := req.FieldMask_Filter()
  resp := new(normal.UserInfoResponse)
  if filter.Masked_Email() {
    resp.Email = "yeqown@gmail.com"
  }
  if filter.Masked_Name() {
    resp.Name = "yeqown"
  }
}

How to debug