...

Source file src/github.com/grpc-ecosystem/grpc-gateway/runtime/fieldmask.go

Documentation: github.com/grpc-ecosystem/grpc-gateway/runtime

     1  package runtime
     2  
     3  import (
     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  
    13  func 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.
    38  func 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  
    62  				var path string
    63  				if item.path == "" {
    64  					path = protoName
    65  				} else {
    66  					path = item.path + "." + protoName
    67  				}
    68  				queue = append(queue, fieldMaskPathItem{path: path, node: v, md: subMd})
    69  			}
    70  		} else if len(item.path) > 0 {
    71  			// otherwise, it's a leaf node so print its path
    72  			fm.Paths = append(fm.Paths, item.path)
    73  		}
    74  	}
    75  
    76  	return fm, nil
    77  }
    78  
    79  // fieldMaskPathItem stores a in-progress deconstruction of a path for a fieldmask
    80  type fieldMaskPathItem struct {
    81  	// the list of prior fields leading up to node connected by dots
    82  	path string
    83  
    84  	// a generic decoded json object the current item to inspect for further path extraction
    85  	node interface{}
    86  
    87  	// descriptor for parent message
    88  	md *descriptor.DescriptorProto
    89  }
    90  

View as plain text