blob: 2b7e01f708321c75b8b88e22e4acfb56d875e7bc [file] [log] [blame]
Jamie Hannaford4baa1232014-09-23 15:23:04 +02001package gophercloud
Jamie Hannafordb3120f52014-09-23 15:17:57 +02002
Ash Wilsone8192ac2014-10-21 09:02:01 -04003import (
Jon Perritt12395212016-02-24 10:41:17 -06004 "bytes"
Ash Wilsone8192ac2014-10-21 09:02:01 -04005 "encoding/json"
Jon Perritt12395212016-02-24 10:41:17 -06006 "io"
Ash Wilsone8192ac2014-10-21 09:02:01 -04007 "net/http"
Jon Perritt66822822016-02-25 03:06:56 -06008 "strconv"
Jon Perritt12395212016-02-24 10:41:17 -06009 "time"
Ash Wilsone8192ac2014-10-21 09:02:01 -040010)
Ash Wilsoneab6a702014-10-20 08:18:30 -040011
Ash Wilson3ce1bd82014-10-31 12:20:00 -040012/*
Ash Wilson64ba49f2014-10-31 15:31:46 -040013Result is an internal type to be used by individual resource packages, but its
14methods will be available on a wide variety of user-facing embedding types.
Ash Wilson3ce1bd82014-10-31 12:20:00 -040015
16It acts as a base struct that other Result types, returned from request
17functions, can embed for convenience. All Results capture basic information
18from the HTTP transaction that was performed, including the response body,
19HTTP headers, and any errors that happened.
20
21Generally, each Result type will have an Extract method that can be used to
22further interpret the result's payload in a specific context. Extensions or
23providers can then provide additional extraction functions to pull out
24provider- or extension-specific information as well.
25*/
Ash Wilsoneab6a702014-10-20 08:18:30 -040026type Result struct {
Ash Wilson3ce1bd82014-10-31 12:20:00 -040027 // Body is the payload of the HTTP response from the server. In most cases,
28 // this will be the deserialized JSON structure.
Ash Wilsond3dc2542014-10-20 10:10:48 -040029 Body interface{}
Ash Wilsoneab6a702014-10-20 08:18:30 -040030
Ash Wilson72e4d2c2014-10-20 10:27:30 -040031 // Header contains the HTTP header structure from the original response.
32 Header http.Header
Ash Wilsoneab6a702014-10-20 08:18:30 -040033
Ash Wilson3ce1bd82014-10-31 12:20:00 -040034 // Err is an error that occurred during the operation. It's deferred until
35 // extraction to make it easier to chain the Extract call.
Ash Wilsoneab6a702014-10-20 08:18:30 -040036 Err error
Jamie Hannafordb3120f52014-09-23 15:17:57 +020037}
Ash Wilsona6b08312014-10-02 15:27:45 -040038
Jon Perritt12395212016-02-24 10:41:17 -060039// ExtractInto allows users to provide an object into which `Extract` will extract
40// the `Result.Body`. This would be useful for OpenStack providers that have
41// different fields in the response object than OpenStack proper.
42func (r Result) ExtractInto(to interface{}) error {
43 if r.Err != nil {
44 return r.Err
45 }
46
47 if reader, ok := r.Body.(io.Reader); ok {
48 if readCloser, ok := reader.(io.Closer); ok {
49 defer readCloser.Close()
50 }
Jon Perritta33da232016-03-02 04:43:08 -060051 return json.NewDecoder(reader).Decode(to)
Jon Perritt12395212016-02-24 10:41:17 -060052 }
53
54 b, err := json.Marshal(r.Body)
55 if err != nil {
56 return err
57 }
58 err = json.Unmarshal(b, to)
59
60 return err
61}
62
Ash Wilson3ce1bd82014-10-31 12:20:00 -040063// PrettyPrintJSON creates a string containing the full response body as
64// pretty-printed JSON. It's useful for capturing test fixtures and for
Ash Wilson0fe6c962014-10-31 15:34:24 -040065// debugging extraction bugs. If you include its output in an issue related to
66// a buggy extraction function, we will all love you forever.
Ash Wilsone8192ac2014-10-21 09:02:01 -040067func (r Result) PrettyPrintJSON() string {
68 pretty, err := json.MarshalIndent(r.Body, "", " ")
69 if err != nil {
70 panic(err.Error())
71 }
72 return string(pretty)
73}
74
Ash Wilson64ba49f2014-10-31 15:31:46 -040075// ErrResult is an internal type to be used by individual resource packages, but
76// its methods will be available on a wide variety of user-facing embedding
77// types.
78//
79// It represents results that only contain a potential error and
Ash Wilson3ce1bd82014-10-31 12:20:00 -040080// nothing else. Usually, if the operation executed successfully, the Err field
81// will be nil; otherwise it will be stocked with a relevant error. Use the
Ash Wilson64ba49f2014-10-31 15:31:46 -040082// ExtractErr method
83// to cleanly pull it out.
Jon Perrittba2395e2014-10-27 15:23:21 -050084type ErrResult struct {
Jon Perritt0c2b0372014-10-27 15:57:29 -050085 Result
Jamie Hannaford021b35c2014-10-27 14:01:53 +010086}
87
Ash Wilson3ce1bd82014-10-31 12:20:00 -040088// ExtractErr is a function that extracts error information, or nil, from a result.
Jon Perrittba2395e2014-10-27 15:23:21 -050089func (r ErrResult) ExtractErr() error {
Jamie Hannaford021b35c2014-10-27 14:01:53 +010090 return r.Err
91}
92
Ash Wilson3ce1bd82014-10-31 12:20:00 -040093/*
Ash Wilson64ba49f2014-10-31 15:31:46 -040094HeaderResult is an internal type to be used by individual resource packages, but
95its methods will be available on a wide variety of user-facing embedding types.
Ash Wilson3ce1bd82014-10-31 12:20:00 -040096
97It represents a result that only contains an error (possibly nil) and an
98http.Header. This is used, for example, by the objectstorage packages in
99openstack, because most of the operations don't return response bodies, but do
100have relevant information in headers.
101*/
Jon Perrittd50f93e2014-10-27 14:19:27 -0500102type HeaderResult struct {
Jon Perritt0c2b0372014-10-27 15:57:29 -0500103 Result
Jon Perrittd50f93e2014-10-27 14:19:27 -0500104}
105
106// ExtractHeader will return the http.Header and error from the HeaderResult.
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400107//
108// header, err := objects.Create(client, "my_container", objects.CreateOpts{}).ExtractHeader()
Jon Perritt66822822016-02-25 03:06:56 -0600109func (r HeaderResult) ExtractInto(to interface{}) error {
110 if r.Err != nil {
111 return r.Err
Jon Perritt63e7a482014-12-04 09:47:23 -0700112 }
Jon Perritt66822822016-02-25 03:06:56 -0600113
114 tmpHeaderMap := map[string]string{}
115 for k, v := range r.Header {
Jon Perritt31b66462016-02-25 22:25:30 -0600116 if len(v) > 0 {
117 tmpHeaderMap[k] = v[0]
118 }
Jon Perritt66822822016-02-25 03:06:56 -0600119 }
120
121 b, err := json.Marshal(tmpHeaderMap)
Jon Perritt63e7a482014-12-04 09:47:23 -0700122 if err != nil {
123 return err
124 }
Jon Perritt66822822016-02-25 03:06:56 -0600125 err = json.Unmarshal(b, to)
126
127 return err
Jon Perritt63e7a482014-12-04 09:47:23 -0700128}
129
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400130// RFC3339Milli describes a common time format used by some API responses.
Ash Wilsona6b08312014-10-02 15:27:45 -0400131const RFC3339Milli = "2006-01-02T15:04:05.999999Z"
Jamie Hannaford369c9c62014-10-08 15:14:43 +0200132
Jon Perritt12395212016-02-24 10:41:17 -0600133type JSONRFC3339Milli time.Time
134
135func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error {
136 b := bytes.NewBuffer(data)
137 dec := json.NewDecoder(b)
138 var s string
139 if err := dec.Decode(&s); err != nil {
140 return err
141 }
142 t, err := time.Parse(RFC3339Milli, s)
143 if err != nil {
144 return err
145 }
146 *jt = JSONRFC3339Milli(t)
147 return nil
148}
149
Jon Perritt66822822016-02-25 03:06:56 -0600150const RFC3339MilliNoZ = "2006-01-02T03:04:05.999999"
151
Jon Perritt12395212016-02-24 10:41:17 -0600152type JSONRFC3339MilliNoZ time.Time
153
154func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error {
Jon Perritt12395212016-02-24 10:41:17 -0600155 var s string
Jon Perritt82583e72016-02-25 06:41:51 -0600156 if err := json.Unmarshal(data, &s); err != nil {
Jon Perritt12395212016-02-24 10:41:17 -0600157 return err
158 }
Jon Perritt82583e72016-02-25 06:41:51 -0600159 if s == "" {
160 return nil
161 }
Jon Perritt12395212016-02-24 10:41:17 -0600162 t, err := time.Parse(RFC3339MilliNoZ, s)
163 if err != nil {
164 return err
165 }
166 *jt = JSONRFC3339MilliNoZ(t)
167 return nil
168}
169
Jon Perritt66822822016-02-25 03:06:56 -0600170type JSONRFC1123 time.Time
171
172func (jt *JSONRFC1123) UnmarshalJSON(data []byte) error {
Jon Perritt66822822016-02-25 03:06:56 -0600173 var s string
Jon Perritt82583e72016-02-25 06:41:51 -0600174 if err := json.Unmarshal(data, &s); err != nil {
Jon Perritt66822822016-02-25 03:06:56 -0600175 return err
176 }
Jon Perritt82583e72016-02-25 06:41:51 -0600177 if s == "" {
178 return nil
179 }
Jon Perritt66822822016-02-25 03:06:56 -0600180 t, err := time.Parse(time.RFC1123, s)
181 if err != nil {
182 return err
183 }
184 *jt = JSONRFC1123(t)
185 return nil
186}
187
188type JSONUnix time.Time
189
190func (jt *JSONUnix) UnmarshalJSON(data []byte) error {
Jon Perritt66822822016-02-25 03:06:56 -0600191 var s string
Jon Perritt82583e72016-02-25 06:41:51 -0600192 if err := json.Unmarshal(data, &s); err != nil {
Jon Perritt66822822016-02-25 03:06:56 -0600193 return err
194 }
Jon Perritt82583e72016-02-25 06:41:51 -0600195 if s == "" {
196 return nil
197 }
Jon Perritt66822822016-02-25 03:06:56 -0600198 unix, err := strconv.ParseInt(s, 10, 64)
199 if err != nil {
200 return err
201 }
202 t = time.Unix(unix, 0)
203 *jt = JSONUnix(t)
204 return nil
205}
206
Jon Perritt31b66462016-02-25 22:25:30 -0600207// RFC3339NoZ is the time format used in Heat (Orchestration).
Jon Perritt66822822016-02-25 03:06:56 -0600208const RFC3339NoZ = "2006-01-02T15:04:05"
209
210type JSONRFC3339NoZ time.Time
211
212func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error {
Jon Perritt66822822016-02-25 03:06:56 -0600213 var s string
Jon Perritt82583e72016-02-25 06:41:51 -0600214 if err := json.Unmarshal(data, &s); err != nil {
Jon Perritt66822822016-02-25 03:06:56 -0600215 return err
216 }
217 if s == "" {
218 return nil
219 }
220 t, err := time.Parse(RFC3339NoZ, s)
221 if err != nil {
222 return err
223 }
224 *jt = JSONRFC3339NoZ(t)
225 return nil
226}
Pratik Mallyae1b6cbb2015-09-09 14:24:14 -0500227
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400228/*
229Link is an internal type to be used in packages of collection resources that are
230paginated in a certain way.
231
232It's a response substructure common to many paginated collection results that is
233used to point to related pages. Usually, the one we care about is the one with
234Rel field set to "next".
235*/
Jamie Hannaford369c9c62014-10-08 15:14:43 +0200236type Link struct {
Jon Perritt12395212016-02-24 10:41:17 -0600237 Href string `json:"href"`
238 Rel string `json:"rel"`
Jamie Hannaford369c9c62014-10-08 15:14:43 +0200239}
240
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400241/*
242ExtractNextURL is an internal function useful for packages of collection
243resources that are paginated in a certain way.
244
jrperrittb1013232016-02-10 19:01:53 -0600245It attempts to extract the "next" URL from slice of Link structs, or
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400246"" if no such URL is present.
247*/
Jamie Hannaford369c9c62014-10-08 15:14:43 +0200248func ExtractNextURL(links []Link) (string, error) {
249 var url string
250
251 for _, l := range links {
252 if l.Rel == "next" {
253 url = l.Href
254 }
255 }
256
257 if url == "" {
258 return "", nil
259 }
260
261 return url, nil
262}