blob: 341aad5a3ea6cec04d128019f739b5c2f071399d [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001package runtime
2
3import (
4 "encoding/json"
5 "io"
6 "strings"
7
8 descriptor2 "github.com/golang/protobuf/descriptor"
9 "github.com/golang/protobuf/protoc-gen-go/descriptor"
10 "google.golang.org/genproto/protobuf/field_mask"
11)
12
13func translateName(name string, md *descriptor.DescriptorProto) (string, *descriptor.DescriptorProto) {
14 // TODO - should really gate this with a test that the marshaller has used json names
15 if md != nil {
16 for _, f := range md.Field {
17 if f.JsonName != nil && f.Name != nil && *f.JsonName == name {
18 var subType *descriptor.DescriptorProto
19
20 // If the field has a TypeName then we retrieve the nested type for translating the embedded message names.
21 if f.TypeName != nil {
22 typeSplit := strings.Split(*f.TypeName, ".")
23 typeName := typeSplit[len(typeSplit)-1]
24 for _, t := range md.NestedType {
25 if typeName == *t.Name {
26 subType = t
27 }
28 }
29 }
30 return *f.Name, subType
31 }
32 }
33 }
34 return name, nil
35}
36
37// FieldMaskFromRequestBody creates a FieldMask printing all complete paths from the JSON body.
38func FieldMaskFromRequestBody(r io.Reader, md *descriptor.DescriptorProto) (*field_mask.FieldMask, error) {
39 fm := &field_mask.FieldMask{}
40 var root interface{}
41 if err := json.NewDecoder(r).Decode(&root); err != nil {
42 if err == io.EOF {
43 return fm, nil
44 }
45 return nil, err
46 }
47
48 queue := []fieldMaskPathItem{{node: root, md: md}}
49 for len(queue) > 0 {
50 // dequeue an item
51 item := queue[0]
52 queue = queue[1:]
53
54 if m, ok := item.node.(map[string]interface{}); ok {
55 // if the item is an object, then enqueue all of its children
56 for k, v := range m {
57 protoName, subMd := translateName(k, item.md)
58 if subMsg, ok := v.(descriptor2.Message); ok {
59 _, subMd = descriptor2.ForMessage(subMsg)
60 }
61 queue = append(queue, fieldMaskPathItem{path: append(item.path, protoName), node: v, md: subMd})
62 }
63 } else if len(item.path) > 0 {
64 // otherwise, it's a leaf node so print its path
65 fm.Paths = append(fm.Paths, strings.Join(item.path, "."))
66 }
67 }
68
69 return fm, nil
70}
71
72// fieldMaskPathItem stores a in-progress deconstruction of a path for a fieldmask
73type fieldMaskPathItem struct {
74 // the list of prior fields leading up to node
75 path []string
76
77 // a generic decoded json object the current item to inspect for further path extraction
78 node interface{}
79
80 // descriptor for parent message
81 md *descriptor.DescriptorProto
82}