github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/results.go (about) 1 package golangsdk 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "strconv" 10 "time" 11 12 "github.com/opentelekomcloud/gophertelekomcloud/internal/extract" 13 ) 14 15 /* 16 Result is an internal type to be used by individual resource packages, but its 17 methods will be available on a wide variety of user-facing embedding types. 18 19 It acts as a base struct that other Result types, returned from request 20 functions, can embed for convenience. All Results capture basic information 21 from the HTTP transaction that was performed, including the response body, 22 HTTP headers, and any errors that happened. 23 24 Generally, each Result type will have an Extract method that can be used to 25 further interpret the result's payload in a specific context. Extensions or 26 providers can then provide additional extraction functions to pull out 27 provider- or extension-specific information as well. 28 29 Deprecated: use functions from internal/extract package instead 30 */ 31 type Result struct { 32 // Body is the payload of the HTTP response from the server. 33 Body []byte 34 35 // Header contains the HTTP header structure from the original response. 36 Header http.Header 37 38 // Err is an error that occurred during the operation. It's deferred until 39 // extraction to make it easier to chain the Extract call. 40 Err error 41 } 42 43 // BodyReader returns cached body as *bytes.Reader 44 func (r Result) BodyReader() io.Reader { 45 return bytes.NewReader(r.Body) 46 } 47 48 type JsonRDSInstanceStatus struct { 49 Instances []JsonRDSInstanceField `json:"instances"` 50 TotalCount int `json:"total_count"` 51 } 52 53 type JsonRDSInstanceField struct { 54 Id string `json:"id"` 55 Name string `json:"name"` 56 Status string `json:"status"` 57 } 58 59 // ExtractInto allows users to provide an object into which `Extract` will extract 60 // the `Result.Body`. This would be useful for OpenStack providers that have 61 // different fields in the response object than OpenStack proper. 62 // 63 // Deprecated: use extract.Into function instead 64 func (r Result) ExtractInto(to interface{}) error { 65 if r.Err != nil { 66 return r.Err 67 } 68 69 return extract.Into(bytes.NewReader(r.Body), to) 70 } 71 72 // ExtractIntoStructPtr will unmarshal the Result (r) into the provided 73 // interface{} (to). 74 // 75 // NOTE: For internal use only 76 // 77 // `to` must be a pointer to an underlying struct type 78 // 79 // If provided, `label` will be filtered out of the response 80 // body prior to `r` being unmarshalled into `to`. 81 // 82 // Deprecated: use extract.IntoStructPtr function instead 83 func (r Result) ExtractIntoStructPtr(to interface{}, label string) error { 84 if r.Err != nil { 85 return r.Err 86 } 87 88 return extract.IntoStructPtr(bytes.NewReader(r.Body), to, label) 89 } 90 91 // ExtractIntoSlicePtr will unmarshal the Result (r) into the provided 92 // interface{} (to). 93 // 94 // NOTE: For internal use only 95 // 96 // `to` must be a pointer to an underlying slice type 97 // 98 // If provided, `label` will be filtered out of the response 99 // body prior to `r` being unmarshalled into `to`. 100 // 101 // Deprecated: use extract.IntoSlicePtr function instead 102 func (r Result) ExtractIntoSlicePtr(to interface{}, label string) error { 103 if r.Err != nil { 104 return r.Err 105 } 106 107 return extract.IntoSlicePtr(bytes.NewReader(r.Body), to, label) 108 } 109 110 func PrettyPrintJSON(body interface{}) string { 111 pretty, err := json.MarshalIndent(body, "", " ") 112 if err != nil { 113 panic(err.Error()) 114 } 115 return string(pretty) 116 } 117 118 // PrettyPrintJSON creates a string containing the full response body as 119 // pretty-printed JSON. It's useful for capturing test fixtures and for 120 // debugging extraction bugs. If you include its output in an issue related to 121 // a buggy extraction function, we will all love you forever. 122 func (r Result) PrettyPrintJSON() string { 123 return PrettyPrintJSON(r.Body) 124 } 125 126 // ErrResult is an internal type to be used by individual resource packages, but 127 // its methods will be available on a wide variety of user-facing embedding 128 // types. 129 // 130 // It represents results that only contain a potential error and 131 // nothing else. Usually, if the operation executed successfully, the Err field 132 // will be nil; otherwise it will be stocked with a relevant error. Use the 133 // ExtractErr method 134 // to cleanly pull it out. 135 // 136 // Deprecated: use plain err return instead 137 type ErrResult struct { 138 Result 139 } 140 141 // ExtractErr is a function that extracts error information, or nil, from a result. 142 func (r ErrResult) ExtractErr() error { 143 return r.Err 144 } 145 146 // ---------------------------------------------------------------------------- 147 148 type ErrRespond struct { 149 ErrorCode string `json:"error_code"` 150 ErrorMsg string `json:"error_msg"` 151 } 152 153 type ErrWithResult struct { 154 ErrResult 155 } 156 157 func (r Result) Extract() (*ErrRespond, error) { 158 var s = ErrRespond{} 159 if r.Err != nil { 160 return nil, r.Err 161 } 162 err := r.ExtractInto(&s) 163 if err != nil { 164 return nil, fmt.Errorf("failed to extract Error with Result") 165 } 166 return &s, nil 167 } 168 169 // ---------------------------------------------------------------------------- 170 171 /* 172 HeaderResult is an internal type to be used by individual resource packages, but 173 its methods will be available on a wide variety of user-facing embedding types. 174 175 It represents a result that only contains an error (possibly nil) and an 176 http.Header. This is used, for example, by the objectstorage packages in 177 openstack, because most of the operations don't return response bodies, but do 178 have relevant information in headers. 179 */ 180 type HeaderResult struct { 181 Result 182 } 183 184 // ExtractInto allows users to provide an object into which `Extract` will 185 // extract the http.Header headers of the result. 186 func (r HeaderResult) ExtractInto(to interface{}) error { 187 if r.Err != nil { 188 return r.Err 189 } 190 191 tmpHeaderMap := map[string]string{} 192 for k, v := range r.Header { 193 if len(v) > 0 { 194 tmpHeaderMap[k] = v[0] 195 } 196 } 197 198 b, err := extract.JsonMarshal(tmpHeaderMap) 199 if err != nil { 200 return err 201 } 202 err = json.Unmarshal(b, to) 203 204 return err 205 } 206 207 // RFC3339Milli describes a common time format used by some API responses. 208 const RFC3339Milli = "2006-01-02T15:04:05.999999Z" 209 210 type JSONRFC3339Milli time.Time 211 212 func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error { 213 b := bytes.NewBuffer(data) 214 dec := json.NewDecoder(b) 215 var s string 216 if err := dec.Decode(&s); err != nil { 217 return err 218 } 219 t, err := time.Parse(RFC3339Milli, s) 220 if err != nil { 221 return err 222 } 223 *jt = JSONRFC3339Milli(t) 224 return nil 225 } 226 227 const RFC3339MilliNoZ = "2006-01-02T15:04:05.999999" 228 229 type JSONRFC3339MilliNoZ time.Time 230 231 func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error { 232 var s string 233 if err := json.Unmarshal(data, &s); err != nil { 234 return err 235 } 236 if s == "" { 237 return nil 238 } 239 t, err := time.Parse(RFC3339MilliNoZ, s) 240 if err != nil { 241 return err 242 } 243 *jt = JSONRFC3339MilliNoZ(t) 244 return nil 245 } 246 247 type JSONRFC1123 time.Time 248 249 func (jt *JSONRFC1123) UnmarshalJSON(data []byte) error { 250 var s string 251 if err := json.Unmarshal(data, &s); err != nil { 252 return err 253 } 254 if s == "" { 255 return nil 256 } 257 t, err := time.Parse(time.RFC1123, s) 258 if err != nil { 259 return err 260 } 261 *jt = JSONRFC1123(t) 262 return nil 263 } 264 265 type JSONUnix time.Time 266 267 func (jt *JSONUnix) UnmarshalJSON(data []byte) error { 268 var s string 269 if err := json.Unmarshal(data, &s); err != nil { 270 return err 271 } 272 if s == "" { 273 return nil 274 } 275 unix, err := strconv.ParseInt(s, 10, 64) 276 if err != nil { 277 return err 278 } 279 t = time.Unix(unix, 0) 280 *jt = JSONUnix(t) 281 return nil 282 } 283 284 // RFC3339NoZ is the time format used in Heat (Orchestration). 285 const RFC3339NoZ = "2006-01-02T15:04:05" 286 287 type JSONRFC3339NoZ time.Time 288 289 func (jt *JSONRFC3339NoZ) UnmarshalJSON(data []byte) error { 290 var s string 291 if err := json.Unmarshal(data, &s); err != nil { 292 return err 293 } 294 if s == "" { 295 return nil 296 } 297 t, err := time.Parse(RFC3339NoZ, s) 298 if err != nil { 299 return err 300 } 301 *jt = JSONRFC3339NoZ(t) 302 return nil 303 } 304 305 // RFC3339ZNoT is the time format used in Zun (Containers Service). 306 const RFC3339ZNoT = "2006-01-02 15:04:05-07:00" 307 308 type JSONRFC3339ZNoT time.Time 309 310 func (jt *JSONRFC3339ZNoT) UnmarshalJSON(data []byte) error { 311 var s string 312 if err := json.Unmarshal(data, &s); err != nil { 313 return err 314 } 315 if s == "" { 316 return nil 317 } 318 t, err := time.Parse(RFC3339ZNoT, s) 319 if err != nil { 320 return err 321 } 322 *jt = JSONRFC3339ZNoT(t) 323 return nil 324 } 325 326 // RFC3339ZNoTNoZ is another time format used in Zun (Containers Service). 327 const RFC3339ZNoTNoZ = "2006-01-02 15:04:05" 328 329 type JSONRFC3339ZNoTNoZ time.Time 330 331 func (jt *JSONRFC3339ZNoTNoZ) UnmarshalJSON(data []byte) error { 332 var s string 333 if err := json.Unmarshal(data, &s); err != nil { 334 return err 335 } 336 if s == "" { 337 return nil 338 } 339 t, err := time.Parse(RFC3339ZNoTNoZ, s) 340 if err != nil { 341 return err 342 } 343 *jt = JSONRFC3339ZNoTNoZ(t) 344 return nil 345 } 346 347 /* 348 Link is an internal type to be used in packages of collection resources that are 349 paginated in a certain way. 350 351 It's a response substructure common to many paginated collection results that is 352 used to point to related pages. Usually, the one we care about is the one with 353 Rel field set to "next". 354 */ 355 type Link struct { 356 Href string `json:"href"` 357 Rel string `json:"rel"` 358 } 359 360 /* 361 ExtractNextURL is an internal function useful for packages of collection 362 resources that are paginated in a certain way. 363 364 It attempts to extract the "next" URL from slice of Link structs, or 365 "" if no such URL is present. 366 */ 367 func ExtractNextURL(links []Link) (string, error) { 368 var url string 369 370 for _, l := range links { 371 if l.Rel == "next" { 372 url = l.Href 373 } 374 } 375 376 if url == "" { 377 return "", nil 378 } 379 380 return url, nil 381 }