blob: 8161a892f49c8e45bbe475b7ec4a775e42d2a424 [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 (
4 "encoding/json"
5 "net/http"
Jon Perritt63e7a482014-12-04 09:47:23 -07006 "reflect"
7
8 "github.com/mitchellh/mapstructure"
Ash Wilsone8192ac2014-10-21 09:02:01 -04009)
Ash Wilsoneab6a702014-10-20 08:18:30 -040010
Ash Wilson3ce1bd82014-10-31 12:20:00 -040011/*
Ash Wilson64ba49f2014-10-31 15:31:46 -040012Result is an internal type to be used by individual resource packages, but its
13methods will be available on a wide variety of user-facing embedding types.
Ash Wilson3ce1bd82014-10-31 12:20:00 -040014
15It acts as a base struct that other Result types, returned from request
16functions, can embed for convenience. All Results capture basic information
17from the HTTP transaction that was performed, including the response body,
18HTTP headers, and any errors that happened.
19
20Generally, each Result type will have an Extract method that can be used to
21further interpret the result's payload in a specific context. Extensions or
22providers can then provide additional extraction functions to pull out
23provider- or extension-specific information as well.
24*/
Ash Wilsoneab6a702014-10-20 08:18:30 -040025type Result struct {
Ash Wilson3ce1bd82014-10-31 12:20:00 -040026 // Body is the payload of the HTTP response from the server. In most cases,
27 // this will be the deserialized JSON structure.
Ash Wilsond3dc2542014-10-20 10:10:48 -040028 Body interface{}
Ash Wilsoneab6a702014-10-20 08:18:30 -040029
Ash Wilson72e4d2c2014-10-20 10:27:30 -040030 // Header contains the HTTP header structure from the original response.
31 Header http.Header
Ash Wilsoneab6a702014-10-20 08:18:30 -040032
Ash Wilson3ce1bd82014-10-31 12:20:00 -040033 // Err is an error that occurred during the operation. It's deferred until
34 // extraction to make it easier to chain the Extract call.
Ash Wilsoneab6a702014-10-20 08:18:30 -040035 Err error
Jamie Hannafordb3120f52014-09-23 15:17:57 +020036}
Ash Wilsona6b08312014-10-02 15:27:45 -040037
Ash Wilson3ce1bd82014-10-31 12:20:00 -040038// PrettyPrintJSON creates a string containing the full response body as
39// pretty-printed JSON. It's useful for capturing test fixtures and for
Ash Wilson0fe6c962014-10-31 15:34:24 -040040// debugging extraction bugs. If you include its output in an issue related to
41// a buggy extraction function, we will all love you forever.
Ash Wilsone8192ac2014-10-21 09:02:01 -040042func (r Result) PrettyPrintJSON() string {
43 pretty, err := json.MarshalIndent(r.Body, "", " ")
44 if err != nil {
45 panic(err.Error())
46 }
47 return string(pretty)
48}
49
Ash Wilson64ba49f2014-10-31 15:31:46 -040050// ErrResult is an internal type to be used by individual resource packages, but
51// its methods will be available on a wide variety of user-facing embedding
52// types.
53//
54// It represents results that only contain a potential error and
Ash Wilson3ce1bd82014-10-31 12:20:00 -040055// nothing else. Usually, if the operation executed successfully, the Err field
56// will be nil; otherwise it will be stocked with a relevant error. Use the
Ash Wilson64ba49f2014-10-31 15:31:46 -040057// ExtractErr method
58// to cleanly pull it out.
Jon Perrittba2395e2014-10-27 15:23:21 -050059type ErrResult struct {
Jon Perritt0c2b0372014-10-27 15:57:29 -050060 Result
Jamie Hannaford021b35c2014-10-27 14:01:53 +010061}
62
Ash Wilson3ce1bd82014-10-31 12:20:00 -040063// ExtractErr is a function that extracts error information, or nil, from a result.
Jon Perrittba2395e2014-10-27 15:23:21 -050064func (r ErrResult) ExtractErr() error {
Jamie Hannaford021b35c2014-10-27 14:01:53 +010065 return r.Err
66}
67
Ash Wilson3ce1bd82014-10-31 12:20:00 -040068/*
Ash Wilson64ba49f2014-10-31 15:31:46 -040069HeaderResult is an internal type to be used by individual resource packages, but
70its methods will be available on a wide variety of user-facing embedding types.
Ash Wilson3ce1bd82014-10-31 12:20:00 -040071
72It represents a result that only contains an error (possibly nil) and an
73http.Header. This is used, for example, by the objectstorage packages in
74openstack, because most of the operations don't return response bodies, but do
75have relevant information in headers.
76*/
Jon Perrittd50f93e2014-10-27 14:19:27 -050077type HeaderResult struct {
Jon Perritt0c2b0372014-10-27 15:57:29 -050078 Result
Jon Perrittd50f93e2014-10-27 14:19:27 -050079}
80
81// ExtractHeader will return the http.Header and error from the HeaderResult.
Ash Wilson3ce1bd82014-10-31 12:20:00 -040082//
83// header, err := objects.Create(client, "my_container", objects.CreateOpts{}).ExtractHeader()
Jon Perrittd50f93e2014-10-27 14:19:27 -050084func (hr HeaderResult) ExtractHeader() (http.Header, error) {
85 return hr.Header, hr.Err
86}
87
Jon Perritt63e7a482014-12-04 09:47:23 -070088// DecodeHeader is a function that decodes a header (usually of type map[string]interface{}) to
89// another type (usually a struct). This function is used by the objectstorage package to give
90// users access to response headers without having to query a map. A DecodeHookFunction is used,
91// because OpenStack-based clients return header values as arrays (Go slices).
92func DecodeHeader(from, to interface{}) error {
93 config := &mapstructure.DecoderConfig{
94 DecodeHook: func(from, to reflect.Kind, data interface{}) (interface{}, error) {
95 if from == reflect.Slice {
96 return data.([]string)[0], nil
97 }
98 return data, nil
99 },
100 Result: to,
101 WeaklyTypedInput: true,
102 }
103 decoder, err := mapstructure.NewDecoder(config)
104 if err != nil {
105 return err
106 }
107 if err := decoder.Decode(from); err != nil {
108 return err
109 }
110 return nil
111}
112
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400113// RFC3339Milli describes a common time format used by some API responses.
Ash Wilsona6b08312014-10-02 15:27:45 -0400114const RFC3339Milli = "2006-01-02T15:04:05.999999Z"
Jamie Hannaford369c9c62014-10-08 15:14:43 +0200115
jrperrittb1013232016-02-10 19:01:53 -0600116// StackFmtTime is the time format used in Heat (Orchestration).
117const StackFmtTime = "2006-01-02T15:04:05"
Pratik Mallyae1b6cbb2015-09-09 14:24:14 -0500118
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400119/*
120Link is an internal type to be used in packages of collection resources that are
121paginated in a certain way.
122
123It's a response substructure common to many paginated collection results that is
124used to point to related pages. Usually, the one we care about is the one with
125Rel field set to "next".
126*/
Jamie Hannaford369c9c62014-10-08 15:14:43 +0200127type Link struct {
128 Href string `mapstructure:"href"`
129 Rel string `mapstructure:"rel"`
130}
131
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400132/*
133ExtractNextURL is an internal function useful for packages of collection
134resources that are paginated in a certain way.
135
jrperrittb1013232016-02-10 19:01:53 -0600136It attempts to extract the "next" URL from slice of Link structs, or
Ash Wilson3ce1bd82014-10-31 12:20:00 -0400137"" if no such URL is present.
138*/
Jamie Hannaford369c9c62014-10-08 15:14:43 +0200139func ExtractNextURL(links []Link) (string, error) {
140 var url string
141
142 for _, l := range links {
143 if l.Rel == "next" {
144 url = l.Href
145 }
146 }
147
148 if url == "" {
149 return "", nil
150 }
151
152 return url, nil
153}