blob: 21c66e3b4f77ad0344e7d83871789e7825e8ef98 [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 Perritt63e7a482014-12-04 09:47:23 -07008 "reflect"
Jon Perritt12395212016-02-24 10:41:17 -06009 "time"
Jon Perritt63e7a482014-12-04 09:47:23 -070010
11 "github.com/mitchellh/mapstructure"
Ash Wilsone8192ac2014-10-21 09:02:01 -040012)
Ash Wilsoneab6a702014-10-20 08:18:30 -040013
Ash Wilson3ce1bd82014-10-31 12:20:00 -040014/*
Ash Wilson64ba49f2014-10-31 15:31:46 -040015Result is an internal type to be used by individual resource packages, but its
16methods will be available on a wide variety of user-facing embedding types.
Ash Wilson3ce1bd82014-10-31 12:20:00 -040017
18It acts as a base struct that other Result types, returned from request
19functions, can embed for convenience. All Results capture basic information
20from the HTTP transaction that was performed, including the response body,
21HTTP headers, and any errors that happened.
22
23Generally, each Result type will have an Extract method that can be used to
24further interpret the result's payload in a specific context. Extensions or
25providers can then provide additional extraction functions to pull out
26provider- or extension-specific information as well.
27*/
Ash Wilsoneab6a702014-10-20 08:18:30 -040028type Result struct {
Ash Wilson3ce1bd82014-10-31 12:20:00 -040029 // Body is the payload of the HTTP response from the server. In most cases,
30 // this will be the deserialized JSON structure.
Ash Wilsond3dc2542014-10-20 10:10:48 -040031 Body interface{}
Ash Wilsoneab6a702014-10-20 08:18:30 -040032
Ash Wilson72e4d2c2014-10-20 10:27:30 -040033 // Header contains the HTTP header structure from the original response.
34 Header http.Header
Ash Wilsoneab6a702014-10-20 08:18:30 -040035
Ash Wilson3ce1bd82014-10-31 12:20:00 -040036 // Err is an error that occurred during the operation. It's deferred until
37 // extraction to make it easier to chain the Extract call.
Ash Wilsoneab6a702014-10-20 08:18:30 -040038 Err error
Jamie Hannafordb3120f52014-09-23 15:17:57 +020039}
Ash Wilsona6b08312014-10-02 15:27:45 -040040
Jon Perritt12395212016-02-24 10:41:17 -060041// 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.
44func (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 jsonDecoder := json.NewDecoder(reader)
54 return jsonDecoder.Decode(to)
55 }
56
57 b, err := json.Marshal(r.Body)
58 if err != nil {
59 return err
60 }
61 err = json.Unmarshal(b, to)
62
63 return err
64}
65
Ash Wilson3ce1bd82014-10-31 12:20:00 -040066// PrettyPrintJSON creates a string containing the full response body as
67// pretty-printed JSON. It's useful for capturing test fixtures and for
Ash Wilson0fe6c962014-10-31 15:34:24 -040068// debugging extraction bugs. If you include its output in an issue related to
69// a buggy extraction function, we will all love you forever.
Ash Wilsone8192ac2014-10-21 09:02:01 -040070func (r Result) PrettyPrintJSON() string {
71 pretty, err := json.MarshalIndent(r.Body, "", " ")
72 if err != nil {
73 panic(err.Error())
74 }
75 return string(pretty)
76}
77
Ash Wilson64ba49f2014-10-31 15:31:46 -040078// ErrResult is an internal type to be used by individual resource packages, but
79// its methods will be available on a wide variety of user-facing embedding
80// types.
81//
82// It represents results that only contain a potential error and
Ash Wilson3ce1bd82014-10-31 12:20:00 -040083// nothing else. Usually, if the operation executed successfully, the Err field
84// will be nil; otherwise it will be stocked with a relevant error. Use the
Ash Wilson64ba49f2014-10-31 15:31:46 -040085// ExtractErr method
86// to cleanly pull it out.
Jon Perrittba2395e2014-10-27 15:23:21 -050087type ErrResult struct {
Jon Perritt0c2b0372014-10-27 15:57:29 -050088 Result
Jamie Hannaford021b35c2014-10-27 14:01:53 +010089}
90
Ash Wilson3ce1bd82014-10-31 12:20:00 -040091// ExtractErr is a function that extracts error information, or nil, from a result.
Jon Perrittba2395e2014-10-27 15:23:21 -050092func (r ErrResult) ExtractErr() error {
Jamie Hannaford021b35c2014-10-27 14:01:53 +010093 return r.Err
94}
95
Ash Wilson3ce1bd82014-10-31 12:20:00 -040096/*
Ash Wilson64ba49f2014-10-31 15:31:46 -040097HeaderResult is an internal type to be used by individual resource packages, but
98its methods will be available on a wide variety of user-facing embedding types.
Ash Wilson3ce1bd82014-10-31 12:20:00 -040099
100It represents a result that only contains an error (possibly nil) and an
101http.Header. This is used, for example, by the objectstorage packages in
102openstack, because most of the operations don't return response bodies, but do
103have relevant information in headers.
104*/
Jon Perrittd50f93e2014-10-27 14:19:27 -0500105type HeaderResult struct {
Jon Perritt0c2b0372014-10-27 15:57:29 -0500106 Result
Jon Perrittd50f93e2014-10-27 14:19:27 -0500107}
108
109// ExtractHeader will return the http.Header and error from the HeaderResult.
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400110//
111// header, err := objects.Create(client, "my_container", objects.CreateOpts{}).ExtractHeader()
Jon Perrittd50f93e2014-10-27 14:19:27 -0500112func (hr HeaderResult) ExtractHeader() (http.Header, error) {
113 return hr.Header, hr.Err
114}
115
Jon Perritt63e7a482014-12-04 09:47:23 -0700116// DecodeHeader is a function that decodes a header (usually of type map[string]interface{}) to
117// another type (usually a struct). This function is used by the objectstorage package to give
118// users access to response headers without having to query a map. A DecodeHookFunction is used,
119// because OpenStack-based clients return header values as arrays (Go slices).
120func DecodeHeader(from, to interface{}) error {
121 config := &mapstructure.DecoderConfig{
122 DecodeHook: func(from, to reflect.Kind, data interface{}) (interface{}, error) {
123 if from == reflect.Slice {
124 return data.([]string)[0], nil
125 }
126 return data, nil
127 },
128 Result: to,
129 WeaklyTypedInput: true,
130 }
131 decoder, err := mapstructure.NewDecoder(config)
132 if err != nil {
133 return err
134 }
135 if err := decoder.Decode(from); err != nil {
136 return err
137 }
138 return nil
139}
140
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400141// RFC3339Milli describes a common time format used by some API responses.
Ash Wilsona6b08312014-10-02 15:27:45 -0400142const RFC3339Milli = "2006-01-02T15:04:05.999999Z"
Jamie Hannaford369c9c62014-10-08 15:14:43 +0200143
Jon Perritt12395212016-02-24 10:41:17 -0600144const RFC3339MilliNoZ = "2006-01-02T03:04:05.999999"
145
146type JSONRFC3339Milli time.Time
147
148func (jt *JSONRFC3339Milli) UnmarshalJSON(data []byte) error {
149 b := bytes.NewBuffer(data)
150 dec := json.NewDecoder(b)
151 var s string
152 if err := dec.Decode(&s); err != nil {
153 return err
154 }
155 t, err := time.Parse(RFC3339Milli, s)
156 if err != nil {
157 return err
158 }
159 *jt = JSONRFC3339Milli(t)
160 return nil
161}
162
163type JSONRFC3339MilliNoZ time.Time
164
165func (jt *JSONRFC3339MilliNoZ) UnmarshalJSON(data []byte) error {
166 b := bytes.NewBuffer(data)
167 dec := json.NewDecoder(b)
168 var s string
169 if err := dec.Decode(&s); err != nil {
170 return err
171 }
172 t, err := time.Parse(RFC3339MilliNoZ, s)
173 if err != nil {
174 return err
175 }
176 *jt = JSONRFC3339MilliNoZ(t)
177 return nil
178}
179
jrperrittb1013232016-02-10 19:01:53 -0600180// StackFmtTime is the time format used in Heat (Orchestration).
181const StackFmtTime = "2006-01-02T15:04:05"
Pratik Mallyae1b6cbb2015-09-09 14:24:14 -0500182
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400183/*
184Link is an internal type to be used in packages of collection resources that are
185paginated in a certain way.
186
187It's a response substructure common to many paginated collection results that is
188used to point to related pages. Usually, the one we care about is the one with
189Rel field set to "next".
190*/
Jamie Hannaford369c9c62014-10-08 15:14:43 +0200191type Link struct {
Jon Perritt12395212016-02-24 10:41:17 -0600192 Href string `json:"href"`
193 Rel string `json:"rel"`
Jamie Hannaford369c9c62014-10-08 15:14:43 +0200194}
195
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400196/*
197ExtractNextURL is an internal function useful for packages of collection
198resources that are paginated in a certain way.
199
jrperrittb1013232016-02-10 19:01:53 -0600200It attempts to extract the "next" URL from slice of Link structs, or
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400201"" if no such URL is present.
202*/
Jamie Hannaford369c9c62014-10-08 15:14:43 +0200203func ExtractNextURL(links []Link) (string, error) {
204 var url string
205
206 for _, l := range links {
207 if l.Rel == "next" {
208 url = l.Href
209 }
210 }
211
212 if url == "" {
213 return "", nil
214 }
215
216 return url, nil
217}