queryutil.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. package queryutil
  2. import (
  3. "encoding/base64"
  4. "fmt"
  5. "net/url"
  6. "reflect"
  7. "sort"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "github.com/aws/aws-sdk-go/private/protocol"
  12. )
  13. // Parse parses an object i and fills a url.Values object. The isEC2 flag
  14. // indicates if this is the EC2 Query sub-protocol.
  15. func Parse(body url.Values, i interface{}, isEC2 bool) error {
  16. q := queryParser{isEC2: isEC2}
  17. return q.parseValue(body, reflect.ValueOf(i), "", "")
  18. }
  19. func elemOf(value reflect.Value) reflect.Value {
  20. for value.Kind() == reflect.Ptr {
  21. value = value.Elem()
  22. }
  23. return value
  24. }
  25. type queryParser struct {
  26. isEC2 bool
  27. }
  28. func (q *queryParser) parseValue(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
  29. value = elemOf(value)
  30. // no need to handle zero values
  31. if !value.IsValid() {
  32. return nil
  33. }
  34. t := tag.Get("type")
  35. if t == "" {
  36. switch value.Kind() {
  37. case reflect.Struct:
  38. t = "structure"
  39. case reflect.Slice:
  40. t = "list"
  41. case reflect.Map:
  42. t = "map"
  43. }
  44. }
  45. switch t {
  46. case "structure":
  47. return q.parseStruct(v, value, prefix)
  48. case "list":
  49. return q.parseList(v, value, prefix, tag)
  50. case "map":
  51. return q.parseMap(v, value, prefix, tag)
  52. default:
  53. return q.parseScalar(v, value, prefix, tag)
  54. }
  55. }
  56. func (q *queryParser) parseStruct(v url.Values, value reflect.Value, prefix string) error {
  57. if !value.IsValid() {
  58. return nil
  59. }
  60. t := value.Type()
  61. for i := 0; i < value.NumField(); i++ {
  62. elemValue := elemOf(value.Field(i))
  63. field := t.Field(i)
  64. if field.PkgPath != "" {
  65. continue // ignore unexported fields
  66. }
  67. if protocol.CanSetIdempotencyToken(value.Field(i), field) {
  68. token := protocol.GetIdempotencyToken()
  69. elemValue = reflect.ValueOf(token)
  70. }
  71. var name string
  72. if q.isEC2 {
  73. name = field.Tag.Get("queryName")
  74. }
  75. if name == "" {
  76. if field.Tag.Get("flattened") != "" && field.Tag.Get("locationNameList") != "" {
  77. name = field.Tag.Get("locationNameList")
  78. } else if locName := field.Tag.Get("locationName"); locName != "" {
  79. name = locName
  80. }
  81. if name != "" && q.isEC2 {
  82. name = strings.ToUpper(name[0:1]) + name[1:]
  83. }
  84. }
  85. if name == "" {
  86. name = field.Name
  87. }
  88. if prefix != "" {
  89. name = prefix + "." + name
  90. }
  91. if err := q.parseValue(v, elemValue, name, field.Tag); err != nil {
  92. return err
  93. }
  94. }
  95. return nil
  96. }
  97. func (q *queryParser) parseList(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
  98. // If it's empty, generate an empty value
  99. if !value.IsNil() && value.Len() == 0 {
  100. v.Set(prefix, "")
  101. return nil
  102. }
  103. // check for unflattened list member
  104. if !q.isEC2 && tag.Get("flattened") == "" {
  105. prefix += ".member"
  106. }
  107. for i := 0; i < value.Len(); i++ {
  108. slicePrefix := prefix
  109. if slicePrefix == "" {
  110. slicePrefix = strconv.Itoa(i + 1)
  111. } else {
  112. slicePrefix = slicePrefix + "." + strconv.Itoa(i+1)
  113. }
  114. if err := q.parseValue(v, value.Index(i), slicePrefix, ""); err != nil {
  115. return err
  116. }
  117. }
  118. return nil
  119. }
  120. func (q *queryParser) parseMap(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
  121. // If it's empty, generate an empty value
  122. if !value.IsNil() && value.Len() == 0 {
  123. v.Set(prefix, "")
  124. return nil
  125. }
  126. // check for unflattened list member
  127. if !q.isEC2 && tag.Get("flattened") == "" {
  128. prefix += ".entry"
  129. }
  130. // sort keys for improved serialization consistency.
  131. // this is not strictly necessary for protocol support.
  132. mapKeyValues := value.MapKeys()
  133. mapKeys := map[string]reflect.Value{}
  134. mapKeyNames := make([]string, len(mapKeyValues))
  135. for i, mapKey := range mapKeyValues {
  136. name := mapKey.String()
  137. mapKeys[name] = mapKey
  138. mapKeyNames[i] = name
  139. }
  140. sort.Strings(mapKeyNames)
  141. for i, mapKeyName := range mapKeyNames {
  142. mapKey := mapKeys[mapKeyName]
  143. mapValue := value.MapIndex(mapKey)
  144. kname := tag.Get("locationNameKey")
  145. if kname == "" {
  146. kname = "key"
  147. }
  148. vname := tag.Get("locationNameValue")
  149. if vname == "" {
  150. vname = "value"
  151. }
  152. // serialize key
  153. var keyName string
  154. if prefix == "" {
  155. keyName = strconv.Itoa(i+1) + "." + kname
  156. } else {
  157. keyName = prefix + "." + strconv.Itoa(i+1) + "." + kname
  158. }
  159. if err := q.parseValue(v, mapKey, keyName, ""); err != nil {
  160. return err
  161. }
  162. // serialize value
  163. var valueName string
  164. if prefix == "" {
  165. valueName = strconv.Itoa(i+1) + "." + vname
  166. } else {
  167. valueName = prefix + "." + strconv.Itoa(i+1) + "." + vname
  168. }
  169. if err := q.parseValue(v, mapValue, valueName, ""); err != nil {
  170. return err
  171. }
  172. }
  173. return nil
  174. }
  175. func (q *queryParser) parseScalar(v url.Values, r reflect.Value, name string, tag reflect.StructTag) error {
  176. switch value := r.Interface().(type) {
  177. case string:
  178. v.Set(name, value)
  179. case []byte:
  180. if !r.IsNil() {
  181. v.Set(name, base64.StdEncoding.EncodeToString(value))
  182. }
  183. case bool:
  184. v.Set(name, strconv.FormatBool(value))
  185. case int64:
  186. v.Set(name, strconv.FormatInt(value, 10))
  187. case int:
  188. v.Set(name, strconv.Itoa(value))
  189. case float64:
  190. v.Set(name, strconv.FormatFloat(value, 'f', -1, 64))
  191. case float32:
  192. v.Set(name, strconv.FormatFloat(float64(value), 'f', -1, 32))
  193. case time.Time:
  194. const ISO8601UTC = "2006-01-02T15:04:05Z"
  195. v.Set(name, value.UTC().Format(ISO8601UTC))
  196. default:
  197. return fmt.Errorf("unsupported value for param %s: %v (%s)", name, r.Interface(), r.Type().Name())
  198. }
  199. return nil
  200. }