More specific types for ObjectStorage response object fields (#74)
* more accurate types for objectstorage response object fields (e.g. ContentLength: string -> in64)
* containers unit tests for new field types
* more specific types for accounts headers fields
* update accounts unit tests
* download header unmarshal method and unit test
* object results unmarshal methods
diff --git a/openstack/objectstorage/v1/objects/results.go b/openstack/objectstorage/v1/objects/results.go
index 3cfe1f4..1390faa 100644
--- a/openstack/objectstorage/v1/objects/results.go
+++ b/openstack/objectstorage/v1/objects/results.go
@@ -1,9 +1,11 @@
package objects
import (
+ "encoding/json"
"fmt"
"io"
"io/ioutil"
+ "strconv"
"strings"
"github.com/gophercloud/gophercloud"
@@ -24,7 +26,7 @@
// LastModified is the RFC3339Milli time the object was last modified, represented
// as a string. For any given object (obj), this value may be parsed to a time.Time:
// lastModified, err := time.Parse(gophercloud.RFC3339Milli, obj.LastModified)
- LastModified string `json:"last_modified"`
+ LastModified gophercloud.JSONRFC1123 `json:"last_modified"`
// Name is the unique name for the object.
Name string `json:"name"`
@@ -101,7 +103,7 @@
AcceptRanges string `json:"Accept-Ranges"`
ContentDisposition string `json:"Content-Disposition"`
ContentEncoding string `json:"Content-Encoding"`
- ContentLength string `json:"Content-Length"`
+ ContentLength int64 `json:"-"`
ContentType string `json:"Content-Type"`
Date gophercloud.JSONRFC1123 `json:"Date"`
DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"`
@@ -112,6 +114,32 @@
TransID string `json:"X-Trans-Id"`
}
+func (h *DownloadHeader) UnmarshalJSON(b []byte) error {
+ type tmp DownloadHeader
+ var hTmp *struct {
+ tmp
+ ContentLength string `json:"Content-Length"`
+ }
+ err := json.Unmarshal(b, &hTmp)
+ if err != nil {
+ return err
+ }
+
+ *h = DownloadHeader(hTmp.tmp)
+
+ switch hTmp.ContentLength {
+ case "":
+ h.ContentLength = 0
+ default:
+ h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// DownloadResult is a *http.Response that is returned from a call to the Download function.
type DownloadResult struct {
gophercloud.HeaderResult
@@ -148,7 +176,7 @@
type GetHeader struct {
ContentDisposition string `json:"Content-Disposition"`
ContentEncoding string `json:"Content-Encoding"`
- ContentLength string `json:"Content-Length"`
+ ContentLength int64 `json:"Content-Length"`
ContentType string `json:"Content-Type"`
Date gophercloud.JSONRFC1123 `json:"Date"`
DeleteAt gophercloud.JSONUnix `json:"X-Delete-At"`
@@ -159,6 +187,32 @@
TransID string `json:"X-Trans-Id"`
}
+func (h *GetHeader) UnmarshalJSON(b []byte) error {
+ type tmp GetHeader
+ var hTmp *struct {
+ tmp
+ ContentLength string `json:"Content-Length"`
+ }
+ err := json.Unmarshal(b, &hTmp)
+ if err != nil {
+ return err
+ }
+
+ *h = GetHeader(hTmp.tmp)
+
+ switch hTmp.ContentLength {
+ case "":
+ h.ContentLength = 0
+ default:
+ h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// GetResult is a *http.Response that is returned from a call to the Get function.
type GetResult struct {
gophercloud.HeaderResult
@@ -190,7 +244,7 @@
// CreateHeader represents the headers returned in the response from a Create request.
type CreateHeader struct {
- ContentLength string `json:"Content-Length"`
+ ContentLength int64 `json:"Content-Length"`
ContentType string `json:"Content-Type"`
Date gophercloud.JSONRFC1123 `json:"Date"`
ETag string `json:"Etag"`
@@ -198,6 +252,32 @@
TransID string `json:"X-Trans-Id"`
}
+func (h *CreateHeader) UnmarshalJSON(b []byte) error {
+ type tmp CreateHeader
+ var hTmp *struct {
+ tmp
+ ContentLength string `json:"Content-Length"`
+ }
+ err := json.Unmarshal(b, &hTmp)
+ if err != nil {
+ return err
+ }
+
+ *h = CreateHeader(hTmp.tmp)
+
+ switch hTmp.ContentLength {
+ case "":
+ h.ContentLength = 0
+ default:
+ h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// CreateResult represents the result of a create operation.
type CreateResult struct {
checksum string
@@ -217,12 +297,38 @@
// UpdateHeader represents the headers returned in the response from a Update request.
type UpdateHeader struct {
- ContentLength string `json:"Content-Length"`
+ ContentLength int64 `json:"Content-Length"`
ContentType string `json:"Content-Type"`
Date gophercloud.JSONRFC1123 `json:"Date"`
TransID string `json:"X-Trans-Id"`
}
+func (h *UpdateHeader) UnmarshalJSON(b []byte) error {
+ type tmp UpdateHeader
+ var hTmp *struct {
+ tmp
+ ContentLength string `json:"Content-Length"`
+ }
+ err := json.Unmarshal(b, &hTmp)
+ if err != nil {
+ return err
+ }
+
+ *h = UpdateHeader(hTmp.tmp)
+
+ switch hTmp.ContentLength {
+ case "":
+ h.ContentLength = 0
+ default:
+ h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// UpdateResult represents the result of an update operation.
type UpdateResult struct {
gophercloud.HeaderResult
@@ -238,12 +344,38 @@
// DeleteHeader represents the headers returned in the response from a Delete request.
type DeleteHeader struct {
- ContentLength string `json:"Content-Length"`
+ ContentLength int64 `json:"Content-Length"`
ContentType string `json:"Content-Type"`
Date gophercloud.JSONRFC1123 `json:"Date"`
TransID string `json:"X-Trans-Id"`
}
+func (h *DeleteHeader) UnmarshalJSON(b []byte) error {
+ type tmp DeleteHeader
+ var hTmp *struct {
+ tmp
+ ContentLength string `json:"Content-Length"`
+ }
+ err := json.Unmarshal(b, &hTmp)
+ if err != nil {
+ return err
+ }
+
+ *h = DeleteHeader(hTmp.tmp)
+
+ switch hTmp.ContentLength {
+ case "":
+ h.ContentLength = 0
+ default:
+ h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// DeleteResult represents the result of a delete operation.
type DeleteResult struct {
gophercloud.HeaderResult
@@ -269,6 +401,32 @@
TransID string `json:"X-Trans-Id"`
}
+func (h *CopyHeader) UnmarshalJSON(b []byte) error {
+ type tmp CopyHeader
+ var hTmp *struct {
+ tmp
+ ContentLength string `json:"Content-Length"`
+ }
+ err := json.Unmarshal(b, &hTmp)
+ if err != nil {
+ return err
+ }
+
+ *h = CopyHeader(hTmp.tmp)
+
+ switch hTmp.ContentLength {
+ case "":
+ h.ContentLength = 0
+ default:
+ h.ContentLength, err = strconv.ParseInt(hTmp.ContentLength, 10, 64)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// CopyResult represents the result of a copy operation.
type CopyResult struct {
gophercloud.HeaderResult
diff --git a/openstack/objectstorage/v1/objects/testing/fixtures.go b/openstack/objectstorage/v1/objects/testing/fixtures.go
index cf1d6c5..d3144cb 100644
--- a/openstack/objectstorage/v1/objects/testing/fixtures.go
+++ b/openstack/objectstorage/v1/objects/testing/fixtures.go
@@ -6,12 +6,18 @@
"io"
"net/http"
"testing"
+ "time"
+ "github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects"
th "github.com/gophercloud/gophercloud/testhelper"
fake "github.com/gophercloud/gophercloud/testhelper/client"
)
+var (
+ loc, _ = time.LoadLocation("GMT")
+)
+
// HandleDownloadObjectSuccessfully creates an HTTP handler at `/testContainer/testObject` on the test handler mux that
// responds with a `Download` response.
func HandleDownloadObjectSuccessfully(t *testing.T) {
@@ -19,6 +25,7 @@
th.TestMethod(t, r, "GET")
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
th.TestHeader(t, r, "Accept", "application/json")
+ w.Header().Set("Date", "Wed, 10 Nov 2009 23:00:00 GMT")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Successful download with Gophercloud")
})
@@ -29,14 +36,14 @@
var ExpectedListInfo = []objects.Object{
{
Hash: "451e372e48e0f6b1114fa0724aa79fa1",
- LastModified: "2009-11-10 23:00:00 +0000 UTC",
+ LastModified: gophercloud.JSONRFC1123(time.Date(2009, time.November, 10, 23, 0, 0, 0, loc)), //"2009-11-10 23:00:00 +0000 UTC"
Bytes: 14,
Name: "goodbye",
ContentType: "application/octet-stream",
},
{
Hash: "451e372e48e0f6b1114fa0724aa79fa1",
- LastModified: "2009-11-10 23:00:00 +0000 UTC",
+ LastModified: gophercloud.JSONRFC1123(time.Date(2009, time.November, 10, 23, 0, 0, 0, loc)),
Bytes: 14,
Name: "hello",
ContentType: "application/octet-stream",
@@ -63,14 +70,14 @@
fmt.Fprintf(w, `[
{
"hash": "451e372e48e0f6b1114fa0724aa79fa1",
- "last_modified": "2009-11-10 23:00:00 +0000 UTC",
+ "last_modified": "Wed, 10 Nov 2009 23:00:00 GMT",
"bytes": 14,
"name": "goodbye",
"content_type": "application/octet-stream"
},
{
"hash": "451e372e48e0f6b1114fa0724aa79fa1",
- "last_modified": "2009-11-10 23:00:00 +0000 UTC",
+ "last_modified": "Wed, 10 Nov 2009 23:00:00 GMT",
"bytes": 14,
"name": "hello",
"content_type": "application/octet-stream"
diff --git a/openstack/objectstorage/v1/objects/testing/requests_test.go b/openstack/objectstorage/v1/objects/testing/requests_test.go
index 2b9306f..7762d08 100644
--- a/openstack/objectstorage/v1/objects/testing/requests_test.go
+++ b/openstack/objectstorage/v1/objects/testing/requests_test.go
@@ -5,7 +5,9 @@
"io"
"strings"
"testing"
+ "time"
+ "github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects"
"github.com/gophercloud/gophercloud/pagination"
th "github.com/gophercloud/gophercloud/testhelper"
@@ -37,6 +39,15 @@
bytes, err := response.ExtractContent()
th.AssertNoErr(t, err)
th.CheckEquals(t, "Successful download with Gophercloud", string(bytes))
+
+ expected := &objects.DownloadHeader{
+ ContentLength: 36,
+ ContentType: "text/plain; charset=utf-8",
+ Date: gophercloud.JSONRFC1123(time.Date(2009, time.November, 10, 23, 0, 0, 0, loc)),
+ }
+ actual, err := response.Extract()
+ th.AssertNoErr(t, err)
+ th.CheckDeepEquals(t, expected, actual)
}
func TestListObjectInfo(t *testing.T) {