Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / gophercloud / gophercloud / params.go
1 package gophercloud
2
3 import (
4         "encoding/json"
5         "fmt"
6         "net/url"
7         "reflect"
8         "strconv"
9         "strings"
10         "time"
11 )
12
13 /*
14 BuildRequestBody builds a map[string]interface from the given `struct`. If
15 parent is not an empty string, the final map[string]interface returned will
16 encapsulate the built one. For example:
17
18   disk := 1
19   createOpts := flavors.CreateOpts{
20     ID:         "1",
21     Name:       "m1.tiny",
22     Disk:       &disk,
23     RAM:        512,
24     VCPUs:      1,
25     RxTxFactor: 1.0,
26   }
27
28   body, err := gophercloud.BuildRequestBody(createOpts, "flavor")
29
30 The above example can be run as-is, however it is recommended to look at how
31 BuildRequestBody is used within Gophercloud to more fully understand how it
32 fits within the request process as a whole rather than use it directly as shown
33 above.
34 */
35 func BuildRequestBody(opts interface{}, parent string) (map[string]interface{}, error) {
36         optsValue := reflect.ValueOf(opts)
37         if optsValue.Kind() == reflect.Ptr {
38                 optsValue = optsValue.Elem()
39         }
40
41         optsType := reflect.TypeOf(opts)
42         if optsType.Kind() == reflect.Ptr {
43                 optsType = optsType.Elem()
44         }
45
46         optsMap := make(map[string]interface{})
47         if optsValue.Kind() == reflect.Struct {
48                 //fmt.Printf("optsValue.Kind() is a reflect.Struct: %+v\n", optsValue.Kind())
49                 for i := 0; i < optsValue.NumField(); i++ {
50                         v := optsValue.Field(i)
51                         f := optsType.Field(i)
52
53                         if f.Name != strings.Title(f.Name) {
54                                 //fmt.Printf("Skipping field: %s...\n", f.Name)
55                                 continue
56                         }
57
58                         //fmt.Printf("Starting on field: %s...\n", f.Name)
59
60                         zero := isZero(v)
61                         //fmt.Printf("v is zero?: %v\n", zero)
62
63                         // if the field has a required tag that's set to "true"
64                         if requiredTag := f.Tag.Get("required"); requiredTag == "true" {
65                                 //fmt.Printf("Checking required field [%s]:\n\tv: %+v\n\tisZero:%v\n", f.Name, v.Interface(), zero)
66                                 // if the field's value is zero, return a missing-argument error
67                                 if zero {
68                                         // if the field has a 'required' tag, it can't have a zero-value
69                                         err := ErrMissingInput{}
70                                         err.Argument = f.Name
71                                         return nil, err
72                                 }
73                         }
74
75                         if xorTag := f.Tag.Get("xor"); xorTag != "" {
76                                 //fmt.Printf("Checking `xor` tag for field [%s] with value %+v:\n\txorTag: %s\n", f.Name, v, xorTag)
77                                 xorField := optsValue.FieldByName(xorTag)
78                                 var xorFieldIsZero bool
79                                 if reflect.ValueOf(xorField.Interface()) == reflect.Zero(xorField.Type()) {
80                                         xorFieldIsZero = true
81                                 } else {
82                                         if xorField.Kind() == reflect.Ptr {
83                                                 xorField = xorField.Elem()
84                                         }
85                                         xorFieldIsZero = isZero(xorField)
86                                 }
87                                 if !(zero != xorFieldIsZero) {
88                                         err := ErrMissingInput{}
89                                         err.Argument = fmt.Sprintf("%s/%s", f.Name, xorTag)
90                                         err.Info = fmt.Sprintf("Exactly one of %s and %s must be provided", f.Name, xorTag)
91                                         return nil, err
92                                 }
93                         }
94
95                         if orTag := f.Tag.Get("or"); orTag != "" {
96                                 //fmt.Printf("Checking `or` tag for field with:\n\tname: %+v\n\torTag:%s\n", f.Name, orTag)
97                                 //fmt.Printf("field is zero?: %v\n", zero)
98                                 if zero {
99                                         orField := optsValue.FieldByName(orTag)
100                                         var orFieldIsZero bool
101                                         if reflect.ValueOf(orField.Interface()) == reflect.Zero(orField.Type()) {
102                                                 orFieldIsZero = true
103                                         } else {
104                                                 if orField.Kind() == reflect.Ptr {
105                                                         orField = orField.Elem()
106                                                 }
107                                                 orFieldIsZero = isZero(orField)
108                                         }
109                                         if orFieldIsZero {
110                                                 err := ErrMissingInput{}
111                                                 err.Argument = fmt.Sprintf("%s/%s", f.Name, orTag)
112                                                 err.Info = fmt.Sprintf("At least one of %s and %s must be provided", f.Name, orTag)
113                                                 return nil, err
114                                         }
115                                 }
116                         }
117
118                         jsonTag := f.Tag.Get("json")
119                         if jsonTag == "-" {
120                                 continue
121                         }
122
123                         if v.Kind() == reflect.Slice || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Slice) {
124                                 sliceValue := v
125                                 if sliceValue.Kind() == reflect.Ptr {
126                                         sliceValue = sliceValue.Elem()
127                                 }
128
129                                 for i := 0; i < sliceValue.Len(); i++ {
130                                         element := sliceValue.Index(i)
131                                         if element.Kind() == reflect.Struct || (element.Kind() == reflect.Ptr && element.Elem().Kind() == reflect.Struct) {
132                                                 _, err := BuildRequestBody(element.Interface(), "")
133                                                 if err != nil {
134                                                         return nil, err
135                                                 }
136                                         }
137                                 }
138                         }
139                         if v.Kind() == reflect.Struct || (v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) {
140                                 if zero {
141                                         //fmt.Printf("value before change: %+v\n", optsValue.Field(i))
142                                         if jsonTag != "" {
143                                                 jsonTagPieces := strings.Split(jsonTag, ",")
144                                                 if len(jsonTagPieces) > 1 && jsonTagPieces[1] == "omitempty" {
145                                                         if v.CanSet() {
146                                                                 if !v.IsNil() {
147                                                                         if v.Kind() == reflect.Ptr {
148                                                                                 v.Set(reflect.Zero(v.Type()))
149                                                                         }
150                                                                 }
151                                                                 //fmt.Printf("value after change: %+v\n", optsValue.Field(i))
152                                                         }
153                                                 }
154                                         }
155                                         continue
156                                 }
157
158                                 //fmt.Printf("Calling BuildRequestBody with:\n\tv: %+v\n\tf.Name:%s\n", v.Interface(), f.Name)
159                                 _, err := BuildRequestBody(v.Interface(), f.Name)
160                                 if err != nil {
161                                         return nil, err
162                                 }
163                         }
164                 }
165
166                 //fmt.Printf("opts: %+v \n", opts)
167
168                 b, err := json.Marshal(opts)
169                 if err != nil {
170                         return nil, err
171                 }
172
173                 //fmt.Printf("string(b): %s\n", string(b))
174
175                 err = json.Unmarshal(b, &optsMap)
176                 if err != nil {
177                         return nil, err
178                 }
179
180                 //fmt.Printf("optsMap: %+v\n", optsMap)
181
182                 if parent != "" {
183                         optsMap = map[string]interface{}{parent: optsMap}
184                 }
185                 //fmt.Printf("optsMap after parent added: %+v\n", optsMap)
186                 return optsMap, nil
187         }
188         // Return an error if the underlying type of 'opts' isn't a struct.
189         return nil, fmt.Errorf("Options type is not a struct.")
190 }
191
192 // EnabledState is a convenience type, mostly used in Create and Update
193 // operations. Because the zero value of a bool is FALSE, we need to use a
194 // pointer instead to indicate zero-ness.
195 type EnabledState *bool
196
197 // Convenience vars for EnabledState values.
198 var (
199         iTrue  = true
200         iFalse = false
201
202         Enabled  EnabledState = &iTrue
203         Disabled EnabledState = &iFalse
204 )
205
206 // IPVersion is a type for the possible IP address versions. Valid instances
207 // are IPv4 and IPv6
208 type IPVersion int
209
210 const (
211         // IPv4 is used for IP version 4 addresses
212         IPv4 IPVersion = 4
213         // IPv6 is used for IP version 6 addresses
214         IPv6 IPVersion = 6
215 )
216
217 // IntToPointer is a function for converting integers into integer pointers.
218 // This is useful when passing in options to operations.
219 func IntToPointer(i int) *int {
220         return &i
221 }
222
223 /*
224 MaybeString is an internal function to be used by request methods in individual
225 resource packages.
226
227 It takes a string that might be a zero value and returns either a pointer to its
228 address or nil. This is useful for allowing users to conveniently omit values
229 from an options struct by leaving them zeroed, but still pass nil to the JSON
230 serializer so they'll be omitted from the request body.
231 */
232 func MaybeString(original string) *string {
233         if original != "" {
234                 return &original
235         }
236         return nil
237 }
238
239 /*
240 MaybeInt is an internal function to be used by request methods in individual
241 resource packages.
242
243 Like MaybeString, it accepts an int that may or may not be a zero value, and
244 returns either a pointer to its address or nil. It's intended to hint that the
245 JSON serializer should omit its field.
246 */
247 func MaybeInt(original int) *int {
248         if original != 0 {
249                 return &original
250         }
251         return nil
252 }
253
254 /*
255 func isUnderlyingStructZero(v reflect.Value) bool {
256         switch v.Kind() {
257         case reflect.Ptr:
258                 return isUnderlyingStructZero(v.Elem())
259         default:
260                 return isZero(v)
261         }
262 }
263 */
264
265 var t time.Time
266
267 func isZero(v reflect.Value) bool {
268         //fmt.Printf("\n\nchecking isZero for value: %+v\n", v)
269         switch v.Kind() {
270         case reflect.Ptr:
271                 if v.IsNil() {
272                         return true
273                 }
274                 return false
275         case reflect.Func, reflect.Map, reflect.Slice:
276                 return v.IsNil()
277         case reflect.Array:
278                 z := true
279                 for i := 0; i < v.Len(); i++ {
280                         z = z && isZero(v.Index(i))
281                 }
282                 return z
283         case reflect.Struct:
284                 if v.Type() == reflect.TypeOf(t) {
285                         if v.Interface().(time.Time).IsZero() {
286                                 return true
287                         }
288                         return false
289                 }
290                 z := true
291                 for i := 0; i < v.NumField(); i++ {
292                         z = z && isZero(v.Field(i))
293                 }
294                 return z
295         }
296         // Compare other types directly:
297         z := reflect.Zero(v.Type())
298         //fmt.Printf("zero type for value: %+v\n\n\n", z)
299         return v.Interface() == z.Interface()
300 }
301
302 /*
303 BuildQueryString is an internal function to be used by request methods in
304 individual resource packages.
305
306 It accepts a tagged structure and expands it into a URL struct. Field names are
307 converted into query parameters based on a "q" tag. For example:
308
309         type struct Something {
310            Bar string `q:"x_bar"`
311            Baz int    `q:"lorem_ipsum"`
312         }
313
314         instance := Something{
315            Bar: "AAA",
316            Baz: "BBB",
317         }
318
319 will be converted into "?x_bar=AAA&lorem_ipsum=BBB".
320
321 The struct's fields may be strings, integers, or boolean values. Fields left at
322 their type's zero value will be omitted from the query.
323 */
324 func BuildQueryString(opts interface{}) (*url.URL, error) {
325         optsValue := reflect.ValueOf(opts)
326         if optsValue.Kind() == reflect.Ptr {
327                 optsValue = optsValue.Elem()
328         }
329
330         optsType := reflect.TypeOf(opts)
331         if optsType.Kind() == reflect.Ptr {
332                 optsType = optsType.Elem()
333         }
334
335         params := url.Values{}
336
337         if optsValue.Kind() == reflect.Struct {
338                 for i := 0; i < optsValue.NumField(); i++ {
339                         v := optsValue.Field(i)
340                         f := optsType.Field(i)
341                         qTag := f.Tag.Get("q")
342
343                         // if the field has a 'q' tag, it goes in the query string
344                         if qTag != "" {
345                                 tags := strings.Split(qTag, ",")
346
347                                 // if the field is set, add it to the slice of query pieces
348                                 if !isZero(v) {
349                                 loop:
350                                         switch v.Kind() {
351                                         case reflect.Ptr:
352                                                 v = v.Elem()
353                                                 goto loop
354                                         case reflect.String:
355                                                 params.Add(tags[0], v.String())
356                                         case reflect.Int:
357                                                 params.Add(tags[0], strconv.FormatInt(v.Int(), 10))
358                                         case reflect.Bool:
359                                                 params.Add(tags[0], strconv.FormatBool(v.Bool()))
360                                         case reflect.Slice:
361                                                 switch v.Type().Elem() {
362                                                 case reflect.TypeOf(0):
363                                                         for i := 0; i < v.Len(); i++ {
364                                                                 params.Add(tags[0], strconv.FormatInt(v.Index(i).Int(), 10))
365                                                         }
366                                                 default:
367                                                         for i := 0; i < v.Len(); i++ {
368                                                                 params.Add(tags[0], v.Index(i).String())
369                                                         }
370                                                 }
371                                         case reflect.Map:
372                                                 if v.Type().Key().Kind() == reflect.String && v.Type().Elem().Kind() == reflect.String {
373                                                         var s []string
374                                                         for _, k := range v.MapKeys() {
375                                                                 value := v.MapIndex(k).String()
376                                                                 s = append(s, fmt.Sprintf("'%s':'%s'", k.String(), value))
377                                                         }
378                                                         params.Add(tags[0], fmt.Sprintf("{%s}", strings.Join(s, ", ")))
379                                                 }
380                                         }
381                                 } else {
382                                         // if the field has a 'required' tag, it can't have a zero-value
383                                         if requiredTag := f.Tag.Get("required"); requiredTag == "true" {
384                                                 return &url.URL{}, fmt.Errorf("Required query parameter [%s] not set.", f.Name)
385                                         }
386                                 }
387                         }
388                 }
389
390                 return &url.URL{RawQuery: params.Encode()}, nil
391         }
392         // Return an error if the underlying type of 'opts' isn't a struct.
393         return nil, fmt.Errorf("Options type is not a struct.")
394 }
395
396 /*
397 BuildHeaders is an internal function to be used by request methods in
398 individual resource packages.
399
400 It accepts an arbitrary tagged structure and produces a string map that's
401 suitable for use as the HTTP headers of an outgoing request. Field names are
402 mapped to header names based in "h" tags.
403
404   type struct Something {
405     Bar string `h:"x_bar"`
406     Baz int    `h:"lorem_ipsum"`
407   }
408
409   instance := Something{
410     Bar: "AAA",
411     Baz: "BBB",
412   }
413
414 will be converted into:
415
416   map[string]string{
417     "x_bar": "AAA",
418     "lorem_ipsum": "BBB",
419   }
420
421 Untagged fields and fields left at their zero values are skipped. Integers,
422 booleans and string values are supported.
423 */
424 func BuildHeaders(opts interface{}) (map[string]string, error) {
425         optsValue := reflect.ValueOf(opts)
426         if optsValue.Kind() == reflect.Ptr {
427                 optsValue = optsValue.Elem()
428         }
429
430         optsType := reflect.TypeOf(opts)
431         if optsType.Kind() == reflect.Ptr {
432                 optsType = optsType.Elem()
433         }
434
435         optsMap := make(map[string]string)
436         if optsValue.Kind() == reflect.Struct {
437                 for i := 0; i < optsValue.NumField(); i++ {
438                         v := optsValue.Field(i)
439                         f := optsType.Field(i)
440                         hTag := f.Tag.Get("h")
441
442                         // if the field has a 'h' tag, it goes in the header
443                         if hTag != "" {
444                                 tags := strings.Split(hTag, ",")
445
446                                 // if the field is set, add it to the slice of query pieces
447                                 if !isZero(v) {
448                                         switch v.Kind() {
449                                         case reflect.String:
450                                                 optsMap[tags[0]] = v.String()
451                                         case reflect.Int:
452                                                 optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10)
453                                         case reflect.Bool:
454                                                 optsMap[tags[0]] = strconv.FormatBool(v.Bool())
455                                         }
456                                 } else {
457                                         // if the field has a 'required' tag, it can't have a zero-value
458                                         if requiredTag := f.Tag.Get("required"); requiredTag == "true" {
459                                                 return optsMap, fmt.Errorf("Required header [%s] not set.", f.Name)
460                                         }
461                                 }
462                         }
463
464                 }
465                 return optsMap, nil
466         }
467         // Return an error if the underlying type of 'opts' isn't a struct.
468         return optsMap, fmt.Errorf("Options type is not a struct.")
469 }
470
471 // IDSliceToQueryString takes a slice of elements and converts them into a query
472 // string. For example, if name=foo and slice=[]int{20, 40, 60}, then the
473 // result would be `?name=20&name=40&name=60'
474 func IDSliceToQueryString(name string, ids []int) string {
475         str := ""
476         for k, v := range ids {
477                 if k == 0 {
478                         str += "?"
479                 } else {
480                         str += "&"
481                 }
482                 str += fmt.Sprintf("%s=%s", name, strconv.Itoa(v))
483         }
484         return str
485 }
486
487 // IntWithinRange returns TRUE if an integer falls within a defined range, and
488 // FALSE if not.
489 func IntWithinRange(val, min, max int) bool {
490         return val > min && val < max
491 }