blob: 4f7020cfd35c23a667b6fefeb0bd7254c0554325 [file] [log] [blame]
Jon Perritt8c93a302014-09-28 22:35:57 -05001package objects
2
3import (
jrperritt655245a2016-08-31 15:30:27 -05004 "encoding/json"
Jon Perritt8c93a302014-09-28 22:35:57 -05005 "fmt"
Jamie Hannaford2e784862014-10-27 10:40:27 +01006 "io"
7 "io/ioutil"
jrperritt655245a2016-08-31 15:30:27 -05008 "strconv"
Jon Perritt8c93a302014-09-28 22:35:57 -05009 "strings"
10
Jon Perritt27249f42016-02-18 10:35:59 -060011 "github.com/gophercloud/gophercloud"
12 "github.com/gophercloud/gophercloud/pagination"
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.
Jon Perritt3c166472016-02-25 03:07:41 -060018 Bytes int64 `json:"bytes"`
Jon Perritt9415ca72014-11-03 11:58:48 -060019
20 // ContentType is the content type of the object.
Jon Perritt3c166472016-02-25 03:07:41 -060021 ContentType string `json:"content_type"`
Jon Perritt9415ca72014-11-03 11:58:48 -060022
23 // Hash represents the MD5 checksum value of the object's content.
Jon Perritt3c166472016-02-25 03:07:41 -060024 Hash string `json:"hash"`
Jon Perritt9415ca72014-11-03 11:58:48 -060025
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)
jrperritt20c08522016-08-31 15:56:38 -050029 LastModified gophercloud.JSONRFC3339MilliNoZ `json:"last_modified"`
Jon Perritt9415ca72014-11-03 11:58:48 -060030
31 // Name is the unique name for the object.
Jon Perritt3c166472016-02-25 03:07:41 -060032 Name string `json:"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)
Jon Perritt3c166472016-02-25 03:07:41 -060044 return len(names) == 0, err
Jon Perritt8c93a302014-09-28 22:35:57 -050045}
46
47// LastMarker returns the last object name in a ListResult.
48func (r ObjectPage) LastMarker() (string, error) {
49 names, err := ExtractNames(r)
50 if err != nil {
51 return "", err
52 }
53 if len(names) == 0 {
54 return "", nil
55 }
56 return names[len(names)-1], nil
57}
58
Jon Perritt8c93a302014-09-28 22:35:57 -050059// ExtractInfo is a function that takes a page of objects and returns their full information.
Jon Perritt3c166472016-02-25 03:07:41 -060060func ExtractInfo(r pagination.Page) ([]Object, error) {
61 var s []Object
62 err := (r.(ObjectPage)).ExtractInto(&s)
63 return s, err
Jon Perritt8c93a302014-09-28 22:35:57 -050064}
65
66// ExtractNames is a function that takes a page of objects and returns only their names.
Jon Perritt3c166472016-02-25 03:07:41 -060067func ExtractNames(r pagination.Page) ([]string, error) {
68 casted := r.(ObjectPage)
Ash Wilson72e4d2c2014-10-20 10:27:30 -040069 ct := casted.Header.Get("Content-Type")
Jon Perritt8c93a302014-09-28 22:35:57 -050070 switch {
71 case strings.HasPrefix(ct, "application/json"):
Jon Perritt3c166472016-02-25 03:07:41 -060072 parsed, err := ExtractInfo(r)
Jon Perritt8c93a302014-09-28 22:35:57 -050073 if err != nil {
74 return nil, err
75 }
76
77 names := make([]string, 0, len(parsed))
78 for _, object := range parsed {
Jon Perritt8aa40262014-09-29 15:41:32 -050079 names = append(names, object.Name)
Jon Perritt8c93a302014-09-28 22:35:57 -050080 }
Jon Perrittfdac6e52014-09-29 19:43:45 -050081
Jon Perritt8c93a302014-09-28 22:35:57 -050082 return names, nil
83 case strings.HasPrefix(ct, "text/plain"):
84 names := make([]string, 0, 50)
85
Jon Perritt3c166472016-02-25 03:07:41 -060086 body := string(r.(ObjectPage).Body.([]uint8))
Jon Perritt8c93a302014-09-28 22:35:57 -050087 for _, name := range strings.Split(body, "\n") {
88 if len(name) > 0 {
89 names = append(names, name)
90 }
91 }
92
93 return names, nil
Jon Perrittfdac6e52014-09-29 19:43:45 -050094 case strings.HasPrefix(ct, "text/html"):
95 return []string{}, nil
Jon Perritt8c93a302014-09-28 22:35:57 -050096 default:
97 return nil, fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct)
98 }
99}
100
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700101// DownloadHeader represents the headers returned in the response from a Download request.
102type DownloadHeader struct {
Jon Perritt3c166472016-02-25 03:07:41 -0600103 AcceptRanges string `json:"Accept-Ranges"`
104 ContentDisposition string `json:"Content-Disposition"`
105 ContentEncoding string `json:"Content-Encoding"`
jrperritt655245a2016-08-31 15:30:27 -0500106 ContentLength int64 `json:"-"`
Jon Perritt3c166472016-02-25 03:07:41 -0600107 ContentType string `json:"Content-Type"`
108 Date gophercloud.JSONRFC1123 `json:"Date"`
109 DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"`
110 ETag string `json:"Etag"`
111 LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"`
112 ObjectManifest string `json:"X-Object-Manifest"`
113 StaticLargeObject bool `json:"X-Static-Large-Object"`
114 TransID string `json:"X-Trans-Id"`
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700115}
116
jrperritt655245a2016-08-31 15:30:27 -0500117func (h *DownloadHeader) UnmarshalJSON(b []byte) error {
118 type tmp DownloadHeader
119 var hTmp *struct {
120 tmp
121 ContentLength string `json:"Content-Length"`
122 }
123 err := json.Unmarshal(b, &hTmp)
124 if err != nil {
125 return err
126 }
127
128 *h = DownloadHeader(hTmp.tmp)
129
130 switch hTmp.ContentLength {
131 case "":
132 h.ContentLength = 0
133 default:
134 h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
135 if err != nil {
136 return err
137 }
138 }
139
140 return nil
141}
142
Jon Perritt5db08922014-09-30 21:32:48 -0500143// DownloadResult is a *http.Response that is returned from a call to the Download function.
144type DownloadResult struct {
Jon Perrittd50f93e2014-10-27 14:19:27 -0500145 gophercloud.HeaderResult
Jamie Hannafordee115552014-10-27 16:11:05 +0100146 Body io.ReadCloser
Jon Perritt5db08922014-09-30 21:32:48 -0500147}
148
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700149// Extract will return a struct of headers returned from a call to Download. To obtain
150// a map of headers, call the ExtractHeader method on the DownloadResult.
Jon Perritt3c166472016-02-25 03:07:41 -0600151func (r DownloadResult) Extract() (*DownloadHeader, error) {
152 var s *DownloadHeader
153 err := r.ExtractInto(&s)
154 return s, err
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700155}
156
Jamie Hannaford2e784862014-10-27 10:40:27 +0100157// ExtractContent is a function that takes a DownloadResult's io.Reader body
158// and reads all available data into a slice of bytes. Please be aware that due
159// the nature of io.Reader is forward-only - meaning that it can only be read
160// once and not rewound. You can recreate a reader from the output of this
161// function by using bytes.NewReader(downloadBytes)
Jon Perritt3c166472016-02-25 03:07:41 -0600162func (r *DownloadResult) ExtractContent() ([]byte, error) {
163 if r.Err != nil {
164 return nil, r.Err
Jon Perritt8c93a302014-09-28 22:35:57 -0500165 }
Jon Perritt3c166472016-02-25 03:07:41 -0600166 defer r.Body.Close()
167 body, err := ioutil.ReadAll(r.Body)
Jamie Hannaford2e784862014-10-27 10:40:27 +0100168 if err != nil {
169 return nil, err
170 }
Jon Perritt3c166472016-02-25 03:07:41 -0600171 r.Body.Close()
Jamie Hannaford2e784862014-10-27 10:40:27 +0100172 return body, nil
Jon Perritt8c93a302014-09-28 22:35:57 -0500173}
174
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700175// GetHeader represents the headers returned in the response from a Get request.
176type GetHeader struct {
Jon Perritt3c166472016-02-25 03:07:41 -0600177 ContentDisposition string `json:"Content-Disposition"`
178 ContentEncoding string `json:"Content-Encoding"`
jrperritt655245a2016-08-31 15:30:27 -0500179 ContentLength int64 `json:"Content-Length"`
Jon Perritt3c166472016-02-25 03:07:41 -0600180 ContentType string `json:"Content-Type"`
181 Date gophercloud.JSONRFC1123 `json:"Date"`
182 DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"`
183 ETag string `json:"Etag"`
184 LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"`
185 ObjectManifest string `json:"X-Object-Manifest"`
186 StaticLargeObject bool `json:"X-Static-Large-Object"`
187 TransID string `json:"X-Trans-Id"`
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700188}
189
jrperritt655245a2016-08-31 15:30:27 -0500190func (h *GetHeader) UnmarshalJSON(b []byte) error {
191 type tmp GetHeader
192 var hTmp *struct {
193 tmp
194 ContentLength string `json:"Content-Length"`
195 }
196 err := json.Unmarshal(b, &hTmp)
197 if err != nil {
198 return err
199 }
200
201 *h = GetHeader(hTmp.tmp)
202
203 switch hTmp.ContentLength {
204 case "":
205 h.ContentLength = 0
206 default:
207 h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
208 if err != nil {
209 return err
210 }
211 }
212
213 return nil
214}
215
Jon Perritt5db08922014-09-30 21:32:48 -0500216// GetResult is a *http.Response that is returned from a call to the Get function.
217type GetResult struct {
Jon Perrittd50f93e2014-10-27 14:19:27 -0500218 gophercloud.HeaderResult
Jon Perritt5db08922014-09-30 21:32:48 -0500219}
220
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700221// Extract will return a struct of headers returned from a call to Get. To obtain
222// a map of headers, call the ExtractHeader method on the GetResult.
Jon Perritt3c166472016-02-25 03:07:41 -0600223func (r GetResult) Extract() (*GetHeader, error) {
224 var s *GetHeader
225 err := r.ExtractInto(&s)
226 return s, err
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700227}
228
Jon Perritt8c93a302014-09-28 22:35:57 -0500229// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
230// and returns the custom metadata associated with the object.
Jon Perritt3c166472016-02-25 03:07:41 -0600231func (r GetResult) ExtractMetadata() (map[string]string, error) {
232 if r.Err != nil {
233 return nil, r.Err
Jon Perritt8c93a302014-09-28 22:35:57 -0500234 }
235 metadata := make(map[string]string)
Jon Perritt3c166472016-02-25 03:07:41 -0600236 for k, v := range r.Header {
Jon Perritt8c93a302014-09-28 22:35:57 -0500237 if strings.HasPrefix(k, "X-Object-Meta-") {
238 key := strings.TrimPrefix(k, "X-Object-Meta-")
239 metadata[key] = v[0]
240 }
241 }
242 return metadata, nil
243}
Jon Perritt5db08922014-09-30 21:32:48 -0500244
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700245// CreateHeader represents the headers returned in the response from a Create request.
246type CreateHeader struct {
jrperritt655245a2016-08-31 15:30:27 -0500247 ContentLength int64 `json:"Content-Length"`
Jon Perritt3c166472016-02-25 03:07:41 -0600248 ContentType string `json:"Content-Type"`
249 Date gophercloud.JSONRFC1123 `json:"Date"`
250 ETag string `json:"Etag"`
251 LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"`
252 TransID string `json:"X-Trans-Id"`
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700253}
254
jrperritt655245a2016-08-31 15:30:27 -0500255func (h *CreateHeader) UnmarshalJSON(b []byte) error {
256 type tmp CreateHeader
257 var hTmp *struct {
258 tmp
259 ContentLength string `json:"Content-Length"`
260 }
261 err := json.Unmarshal(b, &hTmp)
262 if err != nil {
263 return err
264 }
265
266 *h = CreateHeader(hTmp.tmp)
267
268 switch hTmp.ContentLength {
269 case "":
270 h.ContentLength = 0
271 default:
272 h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
273 if err != nil {
274 return err
275 }
276 }
277
278 return nil
279}
280
Jamie Hannafordc9cdc8f2014-10-06 16:32:56 +0200281// CreateResult represents the result of a create operation.
Jon Perritt5db08922014-09-30 21:32:48 -0500282type CreateResult struct {
Jon Perrittfea90732016-03-15 02:57:05 -0500283 checksum string
Jon Perrittd50f93e2014-10-27 14:19:27 -0500284 gophercloud.HeaderResult
Jon Perritt5db08922014-09-30 21:32:48 -0500285}
286
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700287// Extract will return a struct of headers returned from a call to Create. To obtain
288// a map of headers, call the ExtractHeader method on the CreateResult.
Jon Perritt3c166472016-02-25 03:07:41 -0600289func (r CreateResult) Extract() (*CreateHeader, error) {
Jon Perrittfea90732016-03-15 02:57:05 -0500290 //if r.Header.Get("ETag") != fmt.Sprintf("%x", localChecksum) {
291 // return nil, ErrWrongChecksum{}
292 //}
Jon Perritt3c166472016-02-25 03:07:41 -0600293 var s *CreateHeader
294 err := r.ExtractInto(&s)
295 return s, err
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700296}
297
298// UpdateHeader represents the headers returned in the response from a Update request.
299type UpdateHeader struct {
jrperritt655245a2016-08-31 15:30:27 -0500300 ContentLength int64 `json:"Content-Length"`
Jon Perritt3c166472016-02-25 03:07:41 -0600301 ContentType string `json:"Content-Type"`
302 Date gophercloud.JSONRFC1123 `json:"Date"`
303 TransID string `json:"X-Trans-Id"`
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700304}
305
jrperritt655245a2016-08-31 15:30:27 -0500306func (h *UpdateHeader) UnmarshalJSON(b []byte) error {
307 type tmp UpdateHeader
308 var hTmp *struct {
309 tmp
310 ContentLength string `json:"Content-Length"`
311 }
312 err := json.Unmarshal(b, &hTmp)
313 if err != nil {
314 return err
315 }
316
317 *h = UpdateHeader(hTmp.tmp)
318
319 switch hTmp.ContentLength {
320 case "":
321 h.ContentLength = 0
322 default:
323 h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
324 if err != nil {
325 return err
326 }
327 }
328
329 return nil
330}
331
Jamie Hannafordc9cdc8f2014-10-06 16:32:56 +0200332// UpdateResult represents the result of an update operation.
Jon Perritt5db08922014-09-30 21:32:48 -0500333type UpdateResult struct {
Jon Perrittd50f93e2014-10-27 14:19:27 -0500334 gophercloud.HeaderResult
Jon Perritt5db08922014-09-30 21:32:48 -0500335}
336
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700337// Extract will return a struct of headers returned from a call to Update. To obtain
338// a map of headers, call the ExtractHeader method on the UpdateResult.
Jon Perritt3c166472016-02-25 03:07:41 -0600339func (r UpdateResult) Extract() (*UpdateHeader, error) {
340 var s *UpdateHeader
341 err := r.ExtractInto(&s)
342 return s, err
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700343}
344
345// DeleteHeader represents the headers returned in the response from a Delete request.
346type DeleteHeader struct {
jrperritt655245a2016-08-31 15:30:27 -0500347 ContentLength int64 `json:"Content-Length"`
Jon Perritt3c166472016-02-25 03:07:41 -0600348 ContentType string `json:"Content-Type"`
349 Date gophercloud.JSONRFC1123 `json:"Date"`
350 TransID string `json:"X-Trans-Id"`
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700351}
352
jrperritt655245a2016-08-31 15:30:27 -0500353func (h *DeleteHeader) UnmarshalJSON(b []byte) error {
354 type tmp DeleteHeader
355 var hTmp *struct {
356 tmp
357 ContentLength string `json:"Content-Length"`
358 }
359 err := json.Unmarshal(b, &hTmp)
360 if err != nil {
361 return err
362 }
363
364 *h = DeleteHeader(hTmp.tmp)
365
366 switch hTmp.ContentLength {
367 case "":
368 h.ContentLength = 0
369 default:
370 h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
371 if err != nil {
372 return err
373 }
374 }
375
376 return nil
377}
378
Jamie Hannafordc9cdc8f2014-10-06 16:32:56 +0200379// DeleteResult represents the result of a delete operation.
Jon Perritt5db08922014-09-30 21:32:48 -0500380type DeleteResult struct {
Jon Perrittd50f93e2014-10-27 14:19:27 -0500381 gophercloud.HeaderResult
Jon Perritt5db08922014-09-30 21:32:48 -0500382}
383
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700384// Extract will return a struct of headers returned from a call to Delete. To obtain
385// a map of headers, call the ExtractHeader method on the DeleteResult.
Jon Perritt3c166472016-02-25 03:07:41 -0600386func (r DeleteResult) Extract() (*DeleteHeader, error) {
387 var s *DeleteHeader
388 err := r.ExtractInto(&s)
389 return s, err
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700390}
391
392// CopyHeader represents the headers returned in the response from a Copy request.
393type CopyHeader struct {
Jon Perritt3c166472016-02-25 03:07:41 -0600394 ContentLength int64 `json:"Content-Length"`
395 ContentType string `json:"Content-Type"`
396 CopiedFrom string `json:"X-Copied-From"`
397 CopiedFromLastModified gophercloud.JSONRFC1123 `json:"X-Copied-From-Last-Modified"`
398 Date gophercloud.JSONRFC1123 `json:"Date"`
399 ETag string `json:"Etag"`
400 LastModified gophercloud.JSONRFC1123 `json:"Last-Modified"`
401 TransID string `json:"X-Trans-Id"`
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700402}
403
jrperritt655245a2016-08-31 15:30:27 -0500404func (h *CopyHeader) UnmarshalJSON(b []byte) error {
405 type tmp CopyHeader
406 var hTmp *struct {
407 tmp
408 ContentLength string `json:"Content-Length"`
409 }
410 err := json.Unmarshal(b, &hTmp)
411 if err != nil {
412 return err
413 }
414
415 *h = CopyHeader(hTmp.tmp)
416
417 switch hTmp.ContentLength {
418 case "":
419 h.ContentLength = 0
420 default:
421 h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
422 if err != nil {
423 return err
424 }
425 }
426
427 return nil
428}
429
Jamie Hannafordc9cdc8f2014-10-06 16:32:56 +0200430// CopyResult represents the result of a copy operation.
Jon Perritt5db08922014-09-30 21:32:48 -0500431type CopyResult struct {
Jon Perrittd50f93e2014-10-27 14:19:27 -0500432 gophercloud.HeaderResult
Jon Perritt5db08922014-09-30 21:32:48 -0500433}
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700434
435// Extract will return a struct of headers returned from a call to Copy. To obtain
436// a map of headers, call the ExtractHeader method on the CopyResult.
Jon Perritt3c166472016-02-25 03:07:41 -0600437func (r CopyResult) Extract() (*CopyHeader, error) {
438 var s *CopyHeader
439 err := r.ExtractInto(&s)
440 return s, err
Jon Perritt8c31b2a2014-12-03 10:21:11 -0700441}