blob: b51b840c99f69168895dd4ba8ed8788fab015d6b [file] [log] [blame]
Jon Perritt8c93a302014-09-28 22:35:57 -05001package objects
2
3import (
4 "fmt"
Jamie Hannaford2e784862014-10-27 10:40:27 +01005 "io"
6 "io/ioutil"
Jon Perritt8c93a302014-09-28 22:35:57 -05007 "strings"
8
Ash Wilsonaf262872014-10-20 09:32:29 -04009 "github.com/rackspace/gophercloud"
Jon Perritt8c93a302014-09-28 22:35:57 -050010 "github.com/rackspace/gophercloud/pagination"
Jon Perrittea4e3012014-10-09 22:03:19 -050011
12 "github.com/mitchellh/mapstructure"
Jon Perritt8c93a302014-09-28 22:35:57 -050013)
14
15// Object is a structure that holds information related to a storage object.
Jon Perritt8aa40262014-09-29 15:41:32 -050016type Object struct {
Jon Perritt9415ca72014-11-03 11:58:48 -060017 // Bytes is the total number of bytes that comprise the object.
18 Bytes int64 `json:"bytes" mapstructure:"bytes"`
19
20 // ContentType is the content type of the object.
21 ContentType string `json:"content_type" mapstructure:"content_type"`
22
23 // Hash represents the MD5 checksum value of the object's content.
24 Hash string `json:"hash" mapstructure:"hash"`
25
26 // LastModified is the RFC3339Milli time the object was last modified, represented
27 // as a string. For any given object (obj), this value may be parsed to a time.Time:
28 // lastModified, err := time.Parse(gophercloud.RFC3339Milli, obj.LastModified)
Jon Perrittf3171c12014-09-30 17:39:31 -050029 LastModified string `json:"last_modified" mapstructure:"last_modified"`
Jon Perritt9415ca72014-11-03 11:58:48 -060030
31 // Name is the unique name for the object.
32 Name string `json:"name" mapstructure:"name"`
Jon Perritt8aa40262014-09-29 15:41:32 -050033}
Jon Perritt8c93a302014-09-28 22:35:57 -050034
Jamie Hannafordc9cdc8f2014-10-06 16:32:56 +020035// ObjectPage is a single page of objects that is returned from a call to the
36// List function.
Jon Perritt8c93a302014-09-28 22:35:57 -050037type ObjectPage struct {
38 pagination.MarkerPageBase
39}
40
41// IsEmpty returns true if a ListResult contains no object names.
42func (r ObjectPage) IsEmpty() (bool, error) {
43 names, err := ExtractNames(r)
44 if err != nil {
45 return true, err
46 }
47 return len(names) == 0, nil
48}
49
50// LastMarker returns the last object name in a ListResult.
51func (r ObjectPage) LastMarker() (string, error) {
52 names, err := ExtractNames(r)
53 if err != nil {
54 return "", err
55 }
56 if len(names) == 0 {
57 return "", nil
58 }
59 return names[len(names)-1], nil
60}
61
Jon Perritt8c93a302014-09-28 22:35:57 -050062// ExtractInfo is a function that takes a page of objects and returns their full information.
63func ExtractInfo(page pagination.Page) ([]Object, error) {
64 untyped := page.(ObjectPage).Body.([]interface{})
65 results := make([]Object, len(untyped))
66 for index, each := range untyped {
Jon Perritt8aa40262014-09-29 15:41:32 -050067 object := each.(map[string]interface{})
Jon Perrittfdac6e52014-09-29 19:43:45 -050068 err := mapstructure.Decode(object, &results[index])
Jon Perritt8aa40262014-09-29 15:41:32 -050069 if err != nil {
70 return results, err
71 }
Jon Perritt8c93a302014-09-28 22:35:57 -050072 }
73 return results, nil
74}
75
76// ExtractNames is a function that takes a page of objects and returns only their names.
77func ExtractNames(page pagination.Page) ([]string, error) {
78 casted := page.(ObjectPage)
Ash Wilson72e4d2c2014-10-20 10:27:30 -040079 ct := casted.Header.Get("Content-Type")
Jon Perritt8c93a302014-09-28 22:35:57 -050080 switch {
81 case strings.HasPrefix(ct, "application/json"):
82 parsed, err := ExtractInfo(page)
83 if err != nil {
84 return nil, err
85 }
86
87 names := make([]string, 0, len(parsed))
88 for _, object := range parsed {
Jon Perritt8aa40262014-09-29 15:41:32 -050089 names = append(names, object.Name)
Jon Perritt8c93a302014-09-28 22:35:57 -050090 }
Jon Perrittfdac6e52014-09-29 19:43:45 -050091
Jon Perritt8c93a302014-09-28 22:35:57 -050092 return names, nil
93 case strings.HasPrefix(ct, "text/plain"):
94 names := make([]string, 0, 50)
95
96 body := string(page.(ObjectPage).Body.([]uint8))
97 for _, name := range strings.Split(body, "\n") {
98 if len(name) > 0 {
99 names = append(names, name)
100 }
101 }
102
103 return names, nil
Jon Perrittfdac6e52014-09-29 19:43:45 -0500104 case strings.HasPrefix(ct, "text/html"):
105 return []string{}, nil
Jon Perritt8c93a302014-09-28 22:35:57 -0500106 default:
107 return nil, fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct)
108 }
109}
110
Jon Perritt5db08922014-09-30 21:32:48 -0500111// DownloadResult is a *http.Response that is returned from a call to the Download function.
112type DownloadResult struct {
Jon Perrittd50f93e2014-10-27 14:19:27 -0500113 gophercloud.HeaderResult
Jamie Hannafordee115552014-10-27 16:11:05 +0100114 Body io.ReadCloser
Jon Perritt5db08922014-09-30 21:32:48 -0500115}
116
Jamie Hannaford2e784862014-10-27 10:40:27 +0100117// ExtractContent is a function that takes a DownloadResult's io.Reader body
118// and reads all available data into a slice of bytes. Please be aware that due
119// the nature of io.Reader is forward-only - meaning that it can only be read
120// once and not rewound. You can recreate a reader from the output of this
121// function by using bytes.NewReader(downloadBytes)
Jon Perritt8c93a302014-09-28 22:35:57 -0500122func (dr DownloadResult) ExtractContent() ([]byte, error) {
123 if dr.Err != nil {
Ash Wilsonaf262872014-10-20 09:32:29 -0400124 return nil, dr.Err
Jon Perritt8c93a302014-09-28 22:35:57 -0500125 }
Jamie Hannaford2e784862014-10-27 10:40:27 +0100126 body, err := ioutil.ReadAll(dr.Body)
127 if err != nil {
128 return nil, err
129 }
Jamie Hannafordee115552014-10-27 16:11:05 +0100130 dr.Body.Close()
Jamie Hannaford2e784862014-10-27 10:40:27 +0100131 return body, nil
Jon Perritt8c93a302014-09-28 22:35:57 -0500132}
133
Jon Perritt5db08922014-09-30 21:32:48 -0500134// GetResult is a *http.Response that is returned from a call to the Get function.
135type GetResult struct {
Jon Perrittd50f93e2014-10-27 14:19:27 -0500136 gophercloud.HeaderResult
Jon Perritt5db08922014-09-30 21:32:48 -0500137}
138
Jon Perritt8c93a302014-09-28 22:35:57 -0500139// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
140// and returns the custom metadata associated with the object.
141func (gr GetResult) ExtractMetadata() (map[string]string, error) {
142 if gr.Err != nil {
143 return nil, gr.Err
144 }
145 metadata := make(map[string]string)
Ash Wilson72e4d2c2014-10-20 10:27:30 -0400146 for k, v := range gr.Header {
Jon Perritt8c93a302014-09-28 22:35:57 -0500147 if strings.HasPrefix(k, "X-Object-Meta-") {
148 key := strings.TrimPrefix(k, "X-Object-Meta-")
149 metadata[key] = v[0]
150 }
151 }
152 return metadata, nil
153}
Jon Perritt5db08922014-09-30 21:32:48 -0500154
Jamie Hannafordc9cdc8f2014-10-06 16:32:56 +0200155// CreateResult represents the result of a create operation.
Jon Perritt5db08922014-09-30 21:32:48 -0500156type CreateResult struct {
Jon Perrittd50f93e2014-10-27 14:19:27 -0500157 gophercloud.HeaderResult
Jon Perritt5db08922014-09-30 21:32:48 -0500158}
159
Jamie Hannafordc9cdc8f2014-10-06 16:32:56 +0200160// UpdateResult represents the result of an update operation.
Jon Perritt5db08922014-09-30 21:32:48 -0500161type UpdateResult struct {
Jon Perrittd50f93e2014-10-27 14:19:27 -0500162 gophercloud.HeaderResult
Jon Perritt5db08922014-09-30 21:32:48 -0500163}
164
Jamie Hannafordc9cdc8f2014-10-06 16:32:56 +0200165// DeleteResult represents the result of a delete operation.
Jon Perritt5db08922014-09-30 21:32:48 -0500166type DeleteResult struct {
Jon Perrittd50f93e2014-10-27 14:19:27 -0500167 gophercloud.HeaderResult
Jon Perritt5db08922014-09-30 21:32:48 -0500168}
169
Jamie Hannafordc9cdc8f2014-10-06 16:32:56 +0200170// CopyResult represents the result of a copy operation.
Jon Perritt5db08922014-09-30 21:32:48 -0500171type CopyResult struct {
Jon Perrittd50f93e2014-10-27 14:19:27 -0500172 gophercloud.HeaderResult
Jon Perritt5db08922014-09-30 21:32:48 -0500173}