Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / gophercloud / gophercloud / results.go
1 package gophercloud
2
3 import (
4         "bytes"
5         "encoding/json"
6         "fmt"
7         "io"
8         "net/http"
9         "reflect"
10         "strconv"
11         "time"
12 )
13
14 /*
15 Result is an internal type to be used by individual resource packages, but its
16 methods will be available on a wide variety of user-facing embedding types.
17
18 It acts as a base struct that other Result types, returned from request
19 functions, can embed for convenience. All Results capture basic information
20 from the HTTP transaction that was performed, including the response body,
21 HTTP headers, and any errors that happened.
22
23 Generally, each Result type will have an Extract method that can be used to
24 further interpret the result's payload in a specific context. Extensions or
25 providers can then provide additional extraction functions to pull out
26 provider- or extension-specific information as well.
27 */
28 type Result struct {
29         // Body is the payload of the HTTP response from the server. In most cases,
30         // this will be the deserialized JSON structure.
31         Body interface{}
32
33         // Header contains the HTTP header structure from the original response.
34         Header http.Header
35
36         // Err is an error that occurred during the operation. It's deferred until
37         // extraction to make it easier to chain the Extract call.
38         Err error
39 }
40
41 // ExtractInto allows users to provide an object into which `Extract` will extract
42 // the `Result.Body`. This would be useful for OpenStack providers that have
43 // different fields in the response object than OpenStack proper.
44 func (r Result) ExtractInto(to interface{}) error {
45         if r.Err != nil {
46                 return r.Err
47         }
48
49         if reader, ok := r.Body.(io.Reader); ok {
50                 if readCloser, ok := reader.(io.Closer); ok {
51                         defer readCloser.Close()
52                 }
53                 return json.NewDecoder(reader).Decode(to)
54         }
55
56         b, err := json.Marshal(r.Body)
57         if err != nil {
58                 return err
59         }
60         err = json.Unmarshal(b, to)
61
62         return err
63 }
64
65 func (r Result) extractIntoPtr(to interface{}, label string) error {
66         if label == "" {
67                 return r.ExtractInto(&to)
68         }
69
70         var m map[string]interface{}
71         err := r.ExtractInto(&m)
72         if err != nil {
73                 return err
74         }
75
76         b, err := json.Marshal(m[label])
77         if err != nil {
78                 return err
79         }
80
81         toValue := reflect.ValueOf(to)
82         if toValue.Kind() == reflect.Ptr {
83                 toValue = toValue.Elem()
84         }
85
86         switch toValue.Kind() {
87         case reflect.Slice:
88                 typeOfV := toValue.Type().Elem()
89                 if typeOfV.Kind() == reflect.Struct {
90                         if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous {
91                                 newSlice := reflect.MakeSlice(reflect.SliceOf(typeOfV), 0, 0)
92
93                                 if mSlice, ok := m[label].([]interface{}); ok {
94                                         for _, v := range mSlice {
95                                                 // For each iteration of the slice, we create a new struct.
96                                                 // This is to work around a bug where elements of a slice
97                                                 // are reused and not overwritten when the same copy of the
98                                                 // struct is used:
99                                                 //
100                                                 // https://github.com/golang/go/issues/21092
101                                                 // https://github.com/golang/go/issues/24155
102                                                 // https://play.golang.org/p/NHo3ywlPZli
103                                                 newType := reflect.New(typeOfV).Elem()
104
105                                                 b, err := json.Marshal(v)
106                                                 if err != nil {
107                                                         return err
108                                                 }
109
110                                                 // This is needed for structs with an UnmarshalJSON method.
111                                                 // Technically this is just unmarshalling the response into
112                                                 // a struct that is never used, but it's good enough to
113                                                 // trigger the UnmarshalJSON method.
114                                                 for i := 0; i < newType.NumField(); i++ {
115                                                         s := newType.Field(i).Addr().Interface()
116
117                                                         // Unmarshal is used rather than NewDecoder to also work
118                                                         // around the above-mentioned bug.
119                                                         err = json.Unmarshal(b, s)
120                                                         if err != nil {
121                                                                 return err
122                                                         }
123                                                 }
124
125                                                 newSlice = reflect.Append(newSlice, newType)
126                                         }
127                                 }
128
129                                 // "to" should now be properly modeled to receive the
130                                 // JSON response body and unmarshal into all the correct
131                                 // fields of the struct or composed extension struct
132                                 // at the end of this method.
133                                 toValue.Set(newSlice)
134                         }
135                 }
136         case reflect.Struct:
137                 typeOfV := toValue.Type()
138                 if typeOfV.NumField() > 0 && typeOfV.Field(0).Anonymous {
139                         for i := 0; i < toValue.NumField(); i++ {
140                                 toField := toValue.Field(i)
141                                 if toField.Kind() == reflect.Struct {
142                                         s := toField.Addr().Interface()
143                                         err = json.NewDecoder(bytes.NewReader(b)).Decode(s)
144                                         if err != nil {
145                                                 return err
146                                         }
147                                 }
148                         }
149                 }
150         }
151
152         err = json.Unmarshal(b, &to)
153         return err
154 }
155
156 // ExtractIntoStructPtr will unmarshal the Result (r) into the provided
157 // interface{} (to).
158 //
159 // NOTE: For internal use only
160 //
161 // `to` must be a pointer to an underlying struct type
162 //
163 // If provided, `label` will be filtered out of the response
164 // body prior to `r` being unmarshalled into `to`.
165 func (r Result) ExtractIntoStructPtr(to interface{}, label string) error {
166         if r.Err != nil {
167                 return r.Err
168         }
169
170         t := reflect.TypeOf(to)
171         if k := t.Kind(); k != reflect.Ptr {
172                 return fmt.Errorf("Expected pointer, got %v", k)
173         }
174         switch t.Elem().Kind() {
175         case reflect.Struct:
176                 return r.extractIntoPtr(to, label)
177         default:
178                 return fmt.Errorf("Expected pointer to struct, got: %v", t)
179         }
180 }
181
182 // ExtractIntoSlicePtr will unmarshal the Result (r) into the provided
183 // interface{} (to).
184 //
185 // NOTE: For internal use only
186 //
187 // `to` must be a pointer to an underlying slice type
188 //
189 // If provided, `label` will be filtered out of the response
190 // body prior to `r` being unmarshalled into `to`.
191 func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error {
192         if r.Err != nil {
193                 return r.Err
194         }
195
196         t := reflect.TypeOf(to)
197         if k := t.Kind(); k != reflect.Ptr {
198                 return fmt.Errorf("Expected pointer, got %v", k)
199         }
200         switch t.Elem().Kind() {
201         case reflect.Slice:
202                 return r.extractIntoPtr(to, label)
203         default:
204                 return fmt.Errorf("Expected pointer to slice, got: %v", t)
205         }
206 }
207
208 // PrettyPrintJSON creates a string containing the full response body as
209 // pretty-printed JSON. It's useful for capturing test fixtures and for
210 // debugging extraction bugs. If you include its output in an issue related to
211 // a buggy extraction function, we will all love you forever.
212 func (r Result) PrettyPrintJSON() string {
213         pretty, err := json.MarshalIndent(r.Body, "", "  ")
214         if err != nil {
215                 panic(err.Error())
216         }
217         return string(pretty)
218 }
219
220 // ErrResult is an internal type to be used by individual resource packages, but
221 // its methods will be available on a wide variety of user-facing embedding
222 // types.
223 //
224 // It represents results that only contain a potential error and
225 // nothing else. Usually, if the operation executed successfully, the Err field
226 // will be nil; otherwise it will be stocked with a relevant error. Use the
227 // ExtractErr method
228 // to cleanly pull it out.
229 type ErrResult struct {
230         Result
231 }
232
233 // ExtractErr is a function that extracts error information, or nil, from a result.
234 func (r ErrResult) ExtractErr() error {
235         return r.Err
236 }
237
238 /*
239 HeaderResult is an internal type to be used by individual resource packages, but
240 its methods will be available on a wide variety of user-facing embedding types.
241
242 It represents a result that only contains an error (possibly nil) and an
243 http.Header. This is used, for example, by the objectstorage packages in
244 openstack, because most of the operations don't return response bodies, but do
245 have relevant information in headers.
246 */
247 type HeaderResult struct {
248         Result
249 }
250
251 // ExtractInto allows users to provide an object into which `Extract` will
252 // extract the http.Header headers of the result.
253 func (r HeaderResult) ExtractInto(to interface{}) error {
254         if r.Err != nil {
255                 return r.Err
256         }
257
258         tmpHeaderMap := map[string]string{}
259         for k, v := range r.Header {
260                 if len(v) > 0 {
261                         tmpHeaderMap[k] = v[0]
262                 }
263         }
264
265         b, err := json.Marshal(tmpHeaderMap)
266         if err != nil {
267                 return err
268         }
269         err = json.Unmarshal(b, to)
270
271         return err
272 }
273
274 // RFC3339Milli describes a common time format used by some API responses.
275 const RFC3339Milli = "2006-01-02T15:04:05.999999Z"
276
277 type JSONRFC3339Milli time.Time
278
279 func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error {
280         b := bytes.NewBuffer(data)
281         dec := json.NewDecoder(b)
282         var s string
283         if err := dec.Decode(&s); err != nil {
284                 return err
285         }
286         t, err := time.Parse(RFC3339Milli, s)
287         if err != nil {
288                 return err
289         }
290         *jt = JSONRFC3339Milli(t)
291         return nil
292 }
293
294 const RFC3339MilliNoZ = "2006-01-02T15:04:05.999999"
295
296 type JSONRFC3339MilliNoZ time.Time
297
298 func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error {
299         var s string
300         if err := json.Unmarshal(data, &s); err != nil {
301                 return err
302         }
303         if s == "" {
304                 return nil
305         }
306         t, err := time.Parse(RFC3339MilliNoZ, s)
307         if err != nil {
308                 return err
309         }
310         *jt = JSONRFC3339MilliNoZ(t)
311         return nil
312 }
313
314 type JSONRFC1123 time.Time
315
316 func (jt *JSONRFC1123) UnmarshalJSON(data []byte) error {
317         var s string
318         if err := json.Unmarshal(data, &s); err != nil {
319                 return err
320         }
321         if s == "" {
322                 return nil
323         }
324         t, err := time.Parse(time.RFC1123, s)
325         if err != nil {
326                 return err
327         }
328         *jt = JSONRFC1123(t)
329         return nil
330 }
331
332 type JSONUnix time.Time
333
334 func (jt *JSONUnix) UnmarshalJSON(data []byte) error {
335         var s string
336         if err := json.Unmarshal(data, &s); err != nil {
337                 return err
338         }
339         if s == "" {
340                 return nil
341         }
342         unix, err := strconv.ParseInt(s, 10, 64)
343         if err != nil {
344                 return err
345         }
346         t = time.Unix(unix, 0)
347         *jt = JSONUnix(t)
348         return nil
349 }
350
351 // RFC3339NoZ is the time format used in Heat (Orchestration).
352 const RFC3339NoZ = "2006-01-02T15:04:05"
353
354 type JSONRFC3339NoZ time.Time
355
356 func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error {
357         var s string
358         if err := json.Unmarshal(data, &s); err != nil {
359                 return err
360         }
361         if s == "" {
362                 return nil
363         }
364         t, err := time.Parse(RFC3339NoZ, s)
365         if err != nil {
366                 return err
367         }
368         *jt = JSONRFC3339NoZ(t)
369         return nil
370 }
371
372 // RFC3339ZNoT is the time format used in Zun (Containers Service).
373 const RFC3339ZNoT = "2006-01-02 15:04:05-07:00"
374
375 type JSONRFC3339ZNoT time.Time
376
377 func (jt *JSONRFC3339ZNoT) UnmarshalJSON(data []byte) error {
378         var s string
379         if err := json.Unmarshal(data, &s); err != nil {
380                 return err
381         }
382         if s == "" {
383                 return nil
384         }
385         t, err := time.Parse(RFC3339ZNoT, s)
386         if err != nil {
387                 return err
388         }
389         *jt = JSONRFC3339ZNoT(t)
390         return nil
391 }
392
393 // RFC3339ZNoTNoZ is another time format used in Zun (Containers Service).
394 const RFC3339ZNoTNoZ = "2006-01-02 15:04:05"
395
396 type JSONRFC3339ZNoTNoZ time.Time
397
398 func (jt *JSONRFC3339ZNoTNoZ) UnmarshalJSON(data []byte) error {
399         var s string
400         if err := json.Unmarshal(data, &s); err != nil {
401                 return err
402         }
403         if s == "" {
404                 return nil
405         }
406         t, err := time.Parse(RFC3339ZNoTNoZ, s)
407         if err != nil {
408                 return err
409         }
410         *jt = JSONRFC3339ZNoTNoZ(t)
411         return nil
412 }
413
414 /*
415 Link is an internal type to be used in packages of collection resources that are
416 paginated in a certain way.
417
418 It's a response substructure common to many paginated collection results that is
419 used to point to related pages. Usually, the one we care about is the one with
420 Rel field set to "next".
421 */
422 type Link struct {
423         Href string `json:"href"`
424         Rel  string `json:"rel"`
425 }
426
427 /*
428 ExtractNextURL is an internal function useful for packages of collection
429 resources that are paginated in a certain way.
430
431 It attempts to extract the "next" URL from slice of Link structs, or
432 "" if no such URL is present.
433 */
434 func ExtractNextURL(links []Link) (string, error) {
435         var url string
436
437         for _, l := range links {
438                 if l.Rel == "next" {
439                         url = l.Href
440                 }
441         }
442
443         if url == "" {
444                 return "", nil
445         }
446
447         return url, nil
448 }