1package runtime 2 3import ( 4 "encoding/json" 5 "io" 6 "strings" 7 8 "github.com/golang/protobuf/protoc-gen-go/generator" 9 "google.golang.org/genproto/protobuf/field_mask" 10) 11 12// FieldMaskFromRequestBody creates a FieldMask printing all complete paths from the JSON body. 13func FieldMaskFromRequestBody(r io.Reader) (*field_mask.FieldMask, error) { 14 fm := &field_mask.FieldMask{} 15 var root interface{} 16 if err := json.NewDecoder(r).Decode(&root); err != nil { 17 if err == io.EOF { 18 return fm, nil 19 } 20 return nil, err 21 } 22 23 queue := []fieldMaskPathItem{{node: root}} 24 for len(queue) > 0 { 25 // dequeue an item 26 item := queue[0] 27 queue = queue[1:] 28 29 if m, ok := item.node.(map[string]interface{}); ok { 30 // if the item is an object, then enqueue all of its children 31 for k, v := range m { 32 queue = append(queue, fieldMaskPathItem{path: append(item.path, generator.CamelCase(k)), node: v}) 33 } 34 } else if len(item.path) > 0 { 35 // otherwise, it's a leaf node so print its path 36 fm.Paths = append(fm.Paths, strings.Join(item.path, ".")) 37 } 38 } 39 40 return fm, nil 41} 42 43// fieldMaskPathItem stores a in-progress deconstruction of a path for a fieldmask 44type fieldMaskPathItem struct { 45 // the list of prior fields leading up to node 46 path []string 47 48 // a generic decoded json object the current item to inspect for further path extraction 49 node interface{} 50} 51 52// CamelCaseFieldMask updates the given FieldMask by converting all of its paths to CamelCase, using the same heuristic 53// that's used for naming protobuf fields in Go. 54func CamelCaseFieldMask(mask *field_mask.FieldMask) { 55 if mask == nil || mask.Paths == nil { 56 return 57 } 58 59 var newPaths []string 60 for _, path := range mask.Paths { 61 lowerCasedParts := strings.Split(path, ".") 62 var camelCasedParts []string 63 for _, part := range lowerCasedParts { 64 camelCasedParts = append(camelCasedParts, generator.CamelCase(part)) 65 } 66 newPaths = append(newPaths, strings.Join(camelCasedParts, ".")) 67 } 68 69 mask.Paths = newPaths 70} 71