queryutil.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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 field.Tag.Get("ignore") != "" {
  68. continue
  69. }
  70. if protocol.CanSetIdempotencyToken(value.Field(i), field) {
  71. token := protocol.GetIdempotencyToken()
  72. elemValue = reflect.ValueOf(token)
  73. }
  74. var name string
  75. if q.isEC2 {
  76. name = field.Tag.Get("queryName")
  77. }
  78. if name == "" {
  79. if field.Tag.Get("flattened") != "" && field.Tag.Get("locationNameList") != "" {
  80. name = field.Tag.Get("locationNameList")
  81. } else if locName := field.Tag.Get("locationName"); locName != "" {
  82. name = locName
  83. }
  84. if name != "" && q.isEC2 {
  85. name = strings.ToUpper(name[0:1]) + name[1:]
  86. }
  87. }
  88. if name == "" {
  89. name = field.Name
  90. }
  91. if prefix != "" {
  92. name = prefix + "." + name
  93. }
  94. if err := q.parseValue(v, elemValue, name, field.Tag); err != nil {
  95. return err
  96. }
  97. }
  98. return nil
  99. }
  100. func (q *queryParser) parseList(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
  101. // If it's empty, generate an empty value
  102. if !value.IsNil() && value.Len() == 0 {
  103. v.Set(prefix, "")
  104. return nil
  105. }
  106. if _, ok := value.Interface().([]byte); ok {
  107. return q.parseScalar(v, value, prefix, tag)
  108. }
  109. // check for unflattened list member
  110. if !q.isEC2 && tag.Get("flattened") == "" {
  111. if listName := tag.Get("locationNameList"); listName == "" {
  112. prefix += ".member"
  113. } else {
  114. prefix += "." + listName
  115. }
  116. }
  117. for i := 0; i < value.Len(); i++ {
  118. slicePrefix := prefix
  119. if slicePrefix == "" {
  120. slicePrefix = strconv.Itoa(i + 1)
  121. } else {
  122. slicePrefix = slicePrefix + "." + strconv.Itoa(i+1)
  123. }
  124. if err := q.parseValue(v, value.Index(i), slicePrefix, ""); err != nil {
  125. return err
  126. }
  127. }
  128. return nil
  129. }
  130. func (q *queryParser) parseMap(v url.Values, value reflect.Value, prefix string, tag reflect.StructTag) error {
  131. // If it's empty, generate an empty value
  132. if !value.IsNil() && value.Len() == 0 {
  133. v.Set(prefix, "")
  134. return nil
  135. }
  136. // check for unflattened list member
  137. if !q.isEC2 && tag.Get("flattened") == "" {
  138. prefix += ".entry"
  139. }
  140. // sort keys for improved serialization consistency.
  141. // this is not strictly necessary for protocol support.
  142. mapKeyValues := value.MapKeys()
  143. mapKeys := map[string]reflect.Value{}
  144. mapKeyNames := make([]string, len(mapKeyValues))
  145. for i, mapKey := range mapKeyValues {
  146. name := mapKey.String()
  147. mapKeys[name] = mapKey
  148. mapKeyNames[i] = name
  149. }
  150. sort.Strings(mapKeyNames)
  151. for i, mapKeyName := range mapKeyNames {
  152. mapKey := mapKeys[mapKeyName]
  153. mapValue := value.MapIndex(mapKey)
  154. kname := tag.Get("locationNameKey")
  155. if kname == "" {
  156. kname = "key"
  157. }
  158. vname := tag.Get("locationNameValue")
  159. if vname == "" {
  160. vname = "value"
  161. }
  162. // serialize key
  163. var keyName string
  164. if prefix == "" {
  165. keyName = strconv.Itoa(i+1) + "." + kname
  166. } else {
  167. keyName = prefix + "." + strconv.Itoa(i+1) + "." + kname
  168. }
  169. if err := q.parseValue(v, mapKey, keyName, ""); err != nil {
  170. return err
  171. }
  172. // serialize value
  173. var valueName string
  174. if prefix == "" {
  175. valueName = strconv.Itoa(i+1) + "." + vname
  176. } else {
  177. valueName = prefix + "." + strconv.Itoa(i+1) + "." + vname
  178. }
  179. if err := q.parseValue(v, mapValue, valueName, ""); err != nil {
  180. return err
  181. }
  182. }
  183. return nil
  184. }
  185. func (q *queryParser) parseScalar(v url.Values, r reflect.Value, name string, tag reflect.StructTag) error {
  186. switch value := r.Interface().(type) {
  187. case string:
  188. v.Set(name, value)
  189. case []byte:
  190. if !r.IsNil() {
  191. v.Set(name, base64.StdEncoding.EncodeToString(value))
  192. }
  193. case bool:
  194. v.Set(name, strconv.FormatBool(value))
  195. case int64:
  196. v.Set(name, strconv.FormatInt(value, 10))
  197. case int:
  198. v.Set(name, strconv.Itoa(value))
  199. case float64:
  200. v.Set(name, strconv.FormatFloat(value, 'f', -1, 64))
  201. case float32:
  202. v.Set(name, strconv.FormatFloat(float64(value), 'f', -1, 32))
  203. case time.Time:
  204. const ISO8601UTC = "2006-01-02T15:04:05Z"
  205. v.Set(name, value.UTC().Format(ISO8601UTC))
  206. default:
  207. return fmt.Errorf("unsupported value for param %s: %v (%s)", name, r.Interface(), r.Type().Name())
  208. }
  209. return nil
  210. }