query.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package runtime
  2. import (
  3. "fmt"
  4. "net/url"
  5. "reflect"
  6. "strings"
  7. "github.com/golang/protobuf/proto"
  8. "github.com/grpc-ecosystem/grpc-gateway/utilities"
  9. "google.golang.org/grpc/grpclog"
  10. )
  11. // PopulateQueryParameters populates "values" into "msg".
  12. // A value is ignored if its key starts with one of the elements in "filter".
  13. func PopulateQueryParameters(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error {
  14. for key, values := range values {
  15. fieldPath := strings.Split(key, ".")
  16. if filter.HasCommonPrefix(fieldPath) {
  17. continue
  18. }
  19. if err := populateFieldValueFromPath(msg, fieldPath, values); err != nil {
  20. return err
  21. }
  22. }
  23. return nil
  24. }
  25. // PopulateFieldFromPath sets a value in a nested Protobuf structure.
  26. // It instantiates missing protobuf fields as it goes.
  27. func PopulateFieldFromPath(msg proto.Message, fieldPathString string, value string) error {
  28. fieldPath := strings.Split(fieldPathString, ".")
  29. return populateFieldValueFromPath(msg, fieldPath, []string{value})
  30. }
  31. func populateFieldValueFromPath(msg proto.Message, fieldPath []string, values []string) error {
  32. m := reflect.ValueOf(msg)
  33. if m.Kind() != reflect.Ptr {
  34. return fmt.Errorf("unexpected type %T: %v", msg, msg)
  35. }
  36. m = m.Elem()
  37. for i, fieldName := range fieldPath {
  38. isLast := i == len(fieldPath)-1
  39. if !isLast && m.Kind() != reflect.Struct {
  40. return fmt.Errorf("non-aggregate type in the mid of path: %s", strings.Join(fieldPath, "."))
  41. }
  42. f := fieldByProtoName(m, fieldName)
  43. if !f.IsValid() {
  44. grpclog.Printf("field not found in %T: %s", msg, strings.Join(fieldPath, "."))
  45. return nil
  46. }
  47. switch f.Kind() {
  48. case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, reflect.String, reflect.Uint32, reflect.Uint64:
  49. m = f
  50. case reflect.Slice:
  51. // TODO(yugui) Support []byte
  52. if !isLast {
  53. return fmt.Errorf("unexpected repeated field in %s", strings.Join(fieldPath, "."))
  54. }
  55. return populateRepeatedField(f, values)
  56. case reflect.Ptr:
  57. if f.IsNil() {
  58. m = reflect.New(f.Type().Elem())
  59. f.Set(m)
  60. }
  61. m = f.Elem()
  62. continue
  63. case reflect.Struct:
  64. m = f
  65. continue
  66. default:
  67. return fmt.Errorf("unexpected type %s in %T", f.Type(), msg)
  68. }
  69. }
  70. switch len(values) {
  71. case 0:
  72. return fmt.Errorf("no value of field: %s", strings.Join(fieldPath, "."))
  73. case 1:
  74. default:
  75. grpclog.Printf("too many field values: %s", strings.Join(fieldPath, "."))
  76. }
  77. return populateField(m, values[0])
  78. }
  79. // fieldByProtoName looks up a field whose corresponding protobuf field name is "name".
  80. // "m" must be a struct value. It returns zero reflect.Value if no such field found.
  81. func fieldByProtoName(m reflect.Value, name string) reflect.Value {
  82. props := proto.GetProperties(m.Type())
  83. for _, p := range props.Prop {
  84. if p.OrigName == name {
  85. return m.FieldByName(p.Name)
  86. }
  87. }
  88. return reflect.Value{}
  89. }
  90. func populateRepeatedField(f reflect.Value, values []string) error {
  91. elemType := f.Type().Elem()
  92. conv, ok := convFromType[elemType.Kind()]
  93. if !ok {
  94. return fmt.Errorf("unsupported field type %s", elemType)
  95. }
  96. f.Set(reflect.MakeSlice(f.Type(), len(values), len(values)))
  97. for i, v := range values {
  98. result := conv.Call([]reflect.Value{reflect.ValueOf(v)})
  99. if err := result[1].Interface(); err != nil {
  100. return err.(error)
  101. }
  102. f.Index(i).Set(result[0])
  103. }
  104. return nil
  105. }
  106. func populateField(f reflect.Value, value string) error {
  107. conv, ok := convFromType[f.Kind()]
  108. if !ok {
  109. return fmt.Errorf("unsupported field type %T", f)
  110. }
  111. result := conv.Call([]reflect.Value{reflect.ValueOf(value)})
  112. if err := result[1].Interface(); err != nil {
  113. return err.(error)
  114. }
  115. f.Set(result[0])
  116. return nil
  117. }
  118. var (
  119. convFromType = map[reflect.Kind]reflect.Value{
  120. reflect.String: reflect.ValueOf(String),
  121. reflect.Bool: reflect.ValueOf(Bool),
  122. reflect.Float64: reflect.ValueOf(Float64),
  123. reflect.Float32: reflect.ValueOf(Float32),
  124. reflect.Int64: reflect.ValueOf(Int64),
  125. reflect.Int32: reflect.ValueOf(Int32),
  126. reflect.Uint64: reflect.ValueOf(Uint64),
  127. reflect.Uint32: reflect.ValueOf(Uint32),
  128. // TODO(yugui) Support []byte
  129. }
  130. )