Skip to content

Instantly share code, notes, and snippets.

@Kshitij-Dhakal
Last active November 8, 2022 05:28
Show Gist options
  • Save Kshitij-Dhakal/14401df140f23704c09d48c6ca9ac425 to your computer and use it in GitHub Desktop.
Save Kshitij-Dhakal/14401df140f23704c09d48c6ca9ac425 to your computer and use it in GitHub Desktop.
Field Mask Demo
plugins {
id 'java'
id "com.google.protobuf" version "0.9.1"
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'com.google.protobuf:protobuf-java:3.21.9'
implementation 'io.grpc:grpc-all:1.50.2'
runtimeOnly 'com.google.protobuf:protobuf-java-util:3.21.9'
}
test {
useJUnitPlatform()
}
protobuf {
protoc { artifact = "com.google.protobuf:protoc:3.13.0" }
plugins {
grpc { artifact = "io.grpc:protoc-gen-grpc-java:1.31.1" }
}
generateProtoTasks {
all()*.plugins { grpc {} }
}
}
sourceSets {
main {
proto {
srcDirs 'src/main/proto'
}
java {
srcDirs 'build/generated/source/proto/main/grpc'
srcDirs 'build/generated/source/proto/main/java'
}
}
}
tasks.withType(Copy) {
filesMatching("**/*.proto") {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
}
package org.example;
import com.google.protobuf.FieldMask;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.FieldMaskUtil;
import com.google.protobuf.util.JsonFormat;
import org.example.pb.Address;
import org.example.pb.UpdateUserRequest;
import org.example.pb.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.UUID;
public class FieldMaskExample {
private static final Logger log = LoggerFactory.getLogger(FieldMaskExample.class);
public static void main(String[] args) throws InvalidProtocolBufferException {
var entity = User.newBuilder() // fetched from db
.setId(UUID.randomUUID().toString())
.setName("Aubrey Queen")
.setPicture("https://example.org/")
.setEmail("[email protected]")
.setPassword("password")
.setAddress(Address.newBuilder()
.setStreet("Street-1")
.setCity("City-1")
.setState("State-1")
.setZipCode("11111")
.build())
.build();
fetchMaskExample(entity);
updateMaskExample(entity);
}
private static void fetchMaskExample(User entity) throws InvalidProtocolBufferException {
// return only those fields that client requires
var mask = FieldMask.newBuilder()
.addPaths("name")
.addPaths("email")
.addPaths("password")
.addPaths("address.street")
.build();
log.info("{}", JsonFormat.printer().print(FieldMaskUtil.trim(mask, entity)));
// expected :
// {
// "name": "Aubrey Queen",
// "email": "[email protected]",
// "password": "password",
// "address": {
// "street": "Street-1"
// }
// }
//
// Only masked fields are returned and other fields are omitted
}
private static void updateMaskExample(User entity) throws InvalidProtocolBufferException {
// update exactly only those fields which are specified in field mask
// this way we don't need to make multiple patch api for every fields like
// @PATCH("/user/:user-id/name")
// @PATCH("/user/:user-id/picture")
// @PATCH("/user/:user-id/email")
// @PATCH("/user/:user-id/password")
//
// Instead only one PUT API will do the job where we will send update request and mask
// @PUT("/user/:user-id")
var mask = FieldMask.newBuilder()
.addPaths("name")
.addPaths("password")
.build();
// var mask = FieldMaskUtil.fromFieldNumbers(User.class, 2, 5); we can use this also to get same result
var updateUserRequest = UpdateUserRequest.newBuilder()
.setUser(User.newBuilder()
.setName("Sheryl Parsons")
.setPassword("p@55w0rd")
.build())
.setUpdateMask(mask)
.build();
var destination = entity.toBuilder();
FieldMaskUtil.merge(updateUserRequest.getUpdateMask(), // mask to apply
updateUserRequest.getUser(), // source (update request)
destination); // destination (entity fetched from db)
log.info("{}", JsonFormat.printer().print(destination.build()));
// expected :
// {
// "id": "db304f29-fc17-4adc-93a9-fcbf4735192b",
// "name": "Sheryl Parsons",
// "picture": "https://example.org/",
// "email": "[email protected]",
// "password": "p@55w0rd",
// "address": {
// "street": "Street-1",
// "city": "City-1",
// "state": "State-1",
// "zipCode": "11111"
// }
// }
//
// only name and password is updated
}
}
syntax = "proto3";
package org.example;
option go_package = "pb";
option java_package = "org.example.pb";
option java_multiple_files = true;
import "google/protobuf/field_mask.proto";
message Address {
string street = 1;
string city = 2;
string state = 3;
string zipCode = 4;
}
message User {
string id = 1;
string name = 2;
string picture = 3;
string email = 4;
string password = 5;
Address address = 6;
}
message UpdateUserRequest {
// The User resource which replaces the resource on the server.
User User = 1;
// The update mask applies to the resource. For the `FieldMask` definition,
// see https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask
google.protobuf.FieldMask update_mask = 2;
}
@Kshitij-Dhakal
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment