aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--account.go19
-rw-r--r--bulk.go39
-rw-r--r--container.go15
-rw-r--r--container_iterator.go4
-rw-r--r--download.go16
-rw-r--r--errors.go2
-rw-r--r--field_metadata.go10
-rw-r--r--field_string.go10
-rw-r--r--field_time.go34
-rw-r--r--field_uint64.go12
-rw-r--r--generated.go174
-rw-r--r--generated.go.in50
-rw-r--r--headers.go76
-rw-r--r--iterator.go30
-rw-r--r--object.go55
-rw-r--r--object_iterator.go6
-rw-r--r--request.go34
-rw-r--r--tests/account_test.go6
-rw-r--r--tests/bulk_delete_test.go10
-rw-r--r--tests/bulk_upload_test.go6
-rw-r--r--tests/container_iterator_test.go2
-rw-r--r--tests/container_test.go8
-rw-r--r--tests/field_test.go46
-rw-r--r--tests/headers_test.go12
-rw-r--r--tests/object_iterator_test.go4
-rw-r--r--tests/object_test.go36
-rw-r--r--tests/shared_test.go4
27 files changed, 335 insertions, 385 deletions
diff --git a/account.go b/account.go
index 95a2b45..f274701 100644
--- a/account.go
+++ b/account.go
@@ -98,7 +98,7 @@ func (a *Account) Headers() (AccountHeaders, error) {
return AccountHeaders{}, err
}
- headers := AccountHeaders(headersFromHTTP(resp.Header))
+ headers := AccountHeaders{headersFromHTTP(resp.Header)}
err = headers.Validate()
if err != nil {
return headers, err
@@ -113,15 +113,14 @@ func (a *Account) Invalidate() {
a.headers = nil
}
-//Update updates the account using a POST request. To add URL parameters, pass
-//a non-nil *RequestOptions.
+//Update updates the account using a POST request. The headers in the headers
+//attribute take precedence over those in opts.Headers.
//
//A successful POST request implies Invalidate() since it may change metadata.
func (a *Account) Update(headers AccountHeaders, opts *RequestOptions) error {
_, err := Request{
Method: "POST",
- Headers: headersToHTTP(headers),
- Options: opts,
+ Options: cloneRequestOptions(opts, headers.Headers),
ExpectStatusCodes: []int{204},
}.Do(a.backend)
if err == nil {
@@ -130,17 +129,13 @@ func (a *Account) Update(headers AccountHeaders, opts *RequestOptions) error {
return err
}
-//Create creates the account using a PUT request. To add URL parameters, pass
-//a non-nil *RequestOptions.
-//
-//Note that this operation is only available to reseller admins, not to regular
-//users.
+//Create creates the account using a PUT request. This operation is only
+//available to reseller admins, not to regular users.
//
//A successful PUT request implies Invalidate() since it may change metadata.
-func (a *Account) Create(headers AccountHeaders, opts *RequestOptions) error {
+func (a *Account) Create(opts *RequestOptions) error {
_, err := Request{
Method: "PUT",
- Headers: headersToHTTP(headers),
Options: opts,
ExpectStatusCodes: []int{201, 202},
DrainResponseBody: true,
diff --git a/bulk.go b/bulk.go
index 02dbd99..e63366d 100644
--- a/bulk.go
+++ b/bulk.go
@@ -47,11 +47,11 @@ const (
//For example, when uploading an archive that contains the file "a/b/c":
//
// //This uploads the file into the container "a" as object "b/c".
-// account.BulkUpload("", format, contents, nil, nil)
+// account.BulkUpload("", format, contents, nil)
// //This uploads the file into the container "foo" as object "a/b/c".
-// account.BulkUpload("foo", format, contents, nil, nil)
+// account.BulkUpload("foo", format, contents, nil)
// //This uploads the file into the container "foo" as object "bar/baz/a/b/c".
-// account.BulkUpload("foo/bar/baz", format, contents, nil, nil)
+// account.BulkUpload("foo/bar/baz", format, contents, nil)
//
//The first return value indicates the number of files that have been created
//on the server side. This may be lower than the number of files in the archive
@@ -63,7 +63,7 @@ const (
//
//This operation returns (0, ErrNotSupported) if the server does not support
//bulk-uploading.
-func (a *Account) BulkUpload(uploadPath string, format BulkUploadFormat, contents io.Reader, headers AccountHeaders, opts *RequestOptions) (int, error) {
+func (a *Account) BulkUpload(uploadPath string, format BulkUploadFormat, contents io.Reader, opts *RequestOptions) (int, error) {
caps, err := a.Capabilities()
if err != nil {
return 0, err
@@ -75,11 +75,10 @@ func (a *Account) BulkUpload(uploadPath string, format BulkUploadFormat, content
req := Request{
Method: "PUT",
Body: contents,
- Headers: headersToHTTP(headers),
- Options: cloneRequestOptions(opts),
+ Options: cloneRequestOptions(opts, nil),
ExpectStatusCodes: []int{200},
}
- req.Headers.Set("Accept", "application/json")
+ req.Options.Headers.Set("Accept", "application/json")
req.Options.Values.Set("extract-archive", string(format))
fields := strings.SplitN(strings.Trim(uploadPath, "/"), "/", 2)
@@ -134,9 +133,8 @@ func makeBulkObjectError(fullName string, statusCode int) BulkObjectError {
// numDeleted, numNotFound, err := container.Account().BulkDelete(
// objects, []*schwift.Container{container}, nil, nil)
//
-//If the server does not support bulk-deletion, this function will just call
-//Object.Delete() for each given object and Container.Delete() for each given
-//container, and aggregate the result.
+//If the server does not support bulk-deletion, this function falls back to
+//deleting each object and container individually, and aggregates the result.
//
//If not nil, the error return value is *usually* an instance of
//BulkError.
@@ -144,7 +142,7 @@ func makeBulkObjectError(fullName string, statusCode int) BulkObjectError {
//The objects may be located in multiple containers, but they and the
//containers must all be located in the given account. (Otherwise,
//ErrAccountMismatch is returned.)
-func (a *Account) BulkDelete(objects []*Object, containers []*Container, headers AccountHeaders, opts *RequestOptions) (numDeleted int, numNotFound int, deleteError error) {
+func (a *Account) BulkDelete(objects []*Object, containers []*Container, opts *RequestOptions) (numDeleted int, numNotFound int, deleteError error) {
//validate that all given objects are in this account
for _, obj := range objects {
other := obj.Container().Account()
@@ -165,7 +163,7 @@ func (a *Account) BulkDelete(objects []*Object, containers []*Container, headers
return 0, 0, err
}
if caps.BulkDelete == nil {
- return a.bulkDeleteSingle(objects, containers, headers, opts)
+ return a.bulkDeleteSingle(objects, containers, opts)
}
chunkSize := int(caps.BulkDelete.MaximumDeletesPerRequest)
@@ -193,7 +191,7 @@ func (a *Account) BulkDelete(objects []*Object, containers []*Container, headers
chunk := names[0:chunkSize]
names = names[chunkSize:]
- numDeletedNow, numNotFoundNow, err := a.bulkDelete(chunk, headers, opts)
+ numDeletedNow, numNotFoundNow, err := a.bulkDelete(chunk, opts)
numDeleted += numDeletedNow
numNotFound += numNotFoundNow
if err != nil {
@@ -206,7 +204,7 @@ func (a *Account) BulkDelete(objects []*Object, containers []*Container, headers
//Implementation of BulkDelete() for servers that *do not* support bulk
//deletion.
-func (a *Account) bulkDeleteSingle(objects []*Object, containers []*Container, headers AccountHeaders, opts *RequestOptions) (int, int, error) {
+func (a *Account) bulkDeleteSingle(objects []*Object, containers []*Container, opts *RequestOptions) (int, int, error) {
var (
numDeleted = 0
numNotFound = 0
@@ -235,7 +233,7 @@ func (a *Account) bulkDeleteSingle(objects []*Object, containers []*Container, h
}
for _, obj := range objects {
- err := obj.Delete(ObjectHeaders(headers), opts) //this implies Invalidate()
+ err := obj.Delete(opts) //this implies Invalidate()
err = handleSingleError(obj.Container().Name(), obj.Name(), err)
if err != nil {
return numDeleted, numNotFound, err
@@ -243,7 +241,7 @@ func (a *Account) bulkDeleteSingle(objects []*Object, containers []*Container, h
}
for _, container := range containers {
- err := container.Delete(ContainerHeaders(headers), opts) //this implies Invalidate()
+ err := container.Delete(opts) //this implies Invalidate()
err = handleSingleError(container.Name(), "", err)
if err != nil {
return numDeleted, numNotFound, err
@@ -263,16 +261,15 @@ func (a *Account) bulkDeleteSingle(objects []*Object, containers []*Container, h
//Implementation of BulkDelete() for servers that *do* support bulk deletion.
//This function is called *after* chunking, so `len(names) <=
//account.Capabilities.BulkDelete.MaximumDeletesPerRequest`.
-func (a *Account) bulkDelete(names []string, headers AccountHeaders, opts *RequestOptions) (int, int, error) {
+func (a *Account) bulkDelete(names []string, opts *RequestOptions) (int, int, error) {
req := Request{
Method: "DELETE",
Body: strings.NewReader(strings.Join(names, "\n") + "\n"),
- Headers: headersToHTTP(headers),
- Options: cloneRequestOptions(opts),
+ Options: cloneRequestOptions(opts, nil),
ExpectStatusCodes: []int{200},
}
- req.Headers.Set("Accept", "application/json")
- req.Headers.Set("Content-Type", "text/plain")
+ req.Options.Headers.Set("Accept", "application/json")
+ req.Options.Headers.Set("Content-Type", "text/plain")
req.Options.Values.Set("bulk-delete", "true")
resp, err := req.Do(a.backend)
if err != nil {
diff --git a/container.go b/container.go
index 741350e..1d310aa 100644
--- a/container.go
+++ b/container.go
@@ -36,7 +36,7 @@ type Container struct {
//container's existence, or chain this function with the EnsureExists()
//function like so:
//
-// container, err := account.Container("documents").EnsureExists()
+// container, err := account.Container("documents").EnsureExists()
func (a *Account) Container(name string) *Container {
return &Container{a: a, name: name}
}
@@ -81,7 +81,7 @@ func (c *Container) Headers() (ContainerHeaders, error) {
return ContainerHeaders{}, err
}
- headers := ContainerHeaders(headersFromHTTP(resp.Header))
+ headers := ContainerHeaders{headersFromHTTP(resp.Header)}
err = headers.Validate()
if err != nil {
return headers, err
@@ -100,8 +100,7 @@ func (c *Container) Update(headers ContainerHeaders, opts *RequestOptions) error
_, err := Request{
Method: "POST",
ContainerName: c.name,
- Headers: headersToHTTP(headers),
- Options: opts,
+ Options: cloneRequestOptions(opts, headers.Headers),
ExpectStatusCodes: []int{204},
}.Do(c.a.backend)
if err == nil {
@@ -116,11 +115,10 @@ func (c *Container) Update(headers ContainerHeaders, opts *RequestOptions) error
//This function can be used regardless of whether the container exists or not.
//
//A successful PUT request implies Invalidate() since it may change metadata.
-func (c *Container) Create(headers ContainerHeaders, opts *RequestOptions) error {
+func (c *Container) Create(opts *RequestOptions) error {
_, err := Request{
Method: "PUT",
ContainerName: c.name,
- Headers: headersToHTTP(headers),
Options: opts,
ExpectStatusCodes: []int{201, 202},
DrainResponseBody: true,
@@ -139,11 +137,10 @@ func (c *Container) Create(headers ContainerHeaders, opts *RequestOptions) error
//This operation fails with http.StatusNotFound if the container does not exist.
//
//A successful DELETE request implies Invalidate().
-func (c *Container) Delete(headers ContainerHeaders, opts *RequestOptions) error {
+func (c *Container) Delete(opts *RequestOptions) error {
_, err := Request{
Method: "DELETE",
ContainerName: c.name,
- Headers: headersToHTTP(headers),
Options: opts,
ExpectStatusCodes: []int{204},
}.Do(c.a.backend)
@@ -165,7 +162,7 @@ func (c *Container) Invalidate() {
//This function returns the same container again, because its intended use is
//with freshly constructed Container instances like so:
//
-// container, err := account.Container("documents").EnsureExists()
+// container, err := account.Container("documents").EnsureExists()
func (c *Container) EnsureExists() (*Container, error) {
_, err := Request{
Method: "PUT",
diff --git a/container_iterator.go b/container_iterator.go
index c428335..debe475 100644
--- a/container_iterator.go
+++ b/container_iterator.go
@@ -64,9 +64,7 @@ type ContainerIterator struct {
//When Prefix is set, only containers whose name starts with this string are
//returned.
Prefix string
- //Headers may contain additional headers to include with the GET request.
- Headers map[string]string
- //Options may contain additional query parameters for the GET request.
+ //Options may contain additional headers and query parameters for the GET request.
Options *RequestOptions
base *iteratorBase
diff --git a/download.go b/download.go
index 6515bbf..dfd9a07 100644
--- a/download.go
+++ b/download.go
@@ -30,28 +30,26 @@ import (
// var obj *swift.Object
//
// //Do NOT do this!
-// reader, err := obj.Download(nil, nil).AsReadCloser()
+// reader, err := obj.Download(nil).AsReadCloser()
// bytes, err := ioutil.ReadAll(reader)
// err := reader.Close()
// str := string(bytes)
//
// //Do this instead:
-// str, err := obj.Download(nil, nil).AsString()
+// str, err := obj.Download(nil).AsString()
//
-//Since the AsByteSlice and AsString method consume only the unread portion of
-//the ReadCloser, and since they drain the ReadCloser irreversibly, the
-//idiomatic way of using DownloadedObject is to call one of its members
-//immediately, without storing the DownloadedObject instance in a variable
-//first.
+//Since all methods on DownloadedObject are irreversible, the idiomatic way of
+//using DownloadedObject is to call one of its members immediately, without
+//storing the DownloadedObject instance in a variable first.
//
// var obj *swift.Object
//
// //Do NOT do this!
-// downloaded := obj.Download(nil, nil)
+// downloaded := obj.Download(nil)
// reader, err := downloaded.AsReadCloser()
//
// //Do this instead:
-// reader, err := obj.Download(nil, nil).AsReadCloser()
+// reader, err := obj.Download(nil).AsReadCloser()
type DownloadedObject struct {
r io.ReadCloser
err error
diff --git a/errors.go b/errors.go
index d9487c8..5784126 100644
--- a/errors.go
+++ b/errors.go
@@ -116,7 +116,7 @@ func (e BulkError) Error() string {
//Is checks if the given error is an UnexpectedStatusCodeError for that status
//code. For example:
//
-// err := container.Delete(nil, nil)
+// err := container.Delete(nil)
// if err != nil {
// if schwift.Is(err, http.StatusNotFound) {
// //container does not exist -> just what we wanted
diff --git a/field_metadata.go b/field_metadata.go
index 05dee32..90993d4 100644
--- a/field_metadata.go
+++ b/field_metadata.go
@@ -22,12 +22,12 @@ package schwift
//in a headers instance. It cannot be directly constructed, but each headers
//type has a method "Metadata" returning this type. For example:
//
-// hdr := make(ObjectHeaders)
-// //the following two statements are equivalent
-// hdr["X-Object-Meta-Access"] = "strictly confidential"
-// hdr.Metadata().Set("Access", "strictly confidential")
+// hdr := NewObjectHeaders()
+// //the following two statements are equivalent
+// hdr["X-Object-Meta-Access"] = "strictly confidential"
+// hdr.Metadata().Set("Access", "strictly confidential")
type FieldMetadata struct {
- h headerInterface
+ h Headers
k string
}
diff --git a/field_string.go b/field_string.go
index f12d2d5..e0007e9 100644
--- a/field_string.go
+++ b/field_string.go
@@ -22,12 +22,12 @@ package schwift
//whose value is a string. It cannot be directly constructed, but methods on
//the Headers types return this type. For example:
//
-// hdr := make(AccountHeaders)
-// //the following two statements are equivalent:
-// hdr["X-Container-Read"] = ".r:*,.rlistings"
-// hdr.ReadACL().Set(".r:*,.rlistings")
+// hdr := NewAccountHeaders()
+// //the following two statements are equivalent:
+// hdr["X-Container-Read"] = ".r:*,.rlistings"
+// hdr.ReadACL().Set(".r:*,.rlistings")
type FieldString struct {
- h headerInterface
+ h Headers
k string
}
diff --git a/field_time.go b/field_time.go
index 2506f3c..d367f68 100644
--- a/field_time.go
+++ b/field_time.go
@@ -28,25 +28,25 @@ import (
//FieldHTTPTimeReadonly is a helper type that provides type-safe access to a
//readonly Swift header whose value is a HTTP timestamp like this:
//
-// Mon, 02 Jan 2006 15:04:05 GMT
+// Mon, 02 Jan 2006 15:04:05 GMT
//
//It cannot be directly constructed, but methods on the Headers types return
//this type. For example:
//
-// //suppose you have:
-// hdr, err := obj.Headers()
+// //suppose you have:
+// hdr, err := obj.Headers()
//
-// //you could do this:
-// time, err := time.Parse(time.RFC1123, hdr.Get("Last-Modified"))
+// //you could do this:
+// time, err := time.Parse(time.RFC1123, hdr.Get("Last-Modified"))
//
-// //or you can just:
-// time := hdr.UpdatedAt().Get()
+// //or you can just:
+// time := hdr.UpdatedAt().Get()
//
//Don't worry about the missing `err` in the last line. When the header fails
//to parse, Object.Headers() already returns the corresponding
//MalformedHeaderError.
type FieldHTTPTimeReadonly struct {
- h headerInterface
+ h Headers
k string
}
@@ -83,21 +83,21 @@ func (f FieldHTTPTimeReadonly) validate() error {
//header whose value is a UNIX timestamp. It cannot be directly constructed,
//but methods on the Headers types return this type. For example:
//
-// //suppose you have:
-// hdr, err := obj.Headers()
+// //suppose you have:
+// hdr, err := obj.Headers()
//
-// //you could do all this:
-// sec, err := strconv.ParseFloat(hdr.Get("X-Delete-At"), 64)
-// time := time.Unix(0, int64(1e9 * sec))
+// //you could do all this:
+// sec, err := strconv.ParseFloat(hdr.Get("X-Delete-At"), 64)
+// time := time.Unix(0, int64(1e9 * sec))
//
-// //or you can just:
-// time := hdr.ExpiresAt().Get()
+// //or you can just:
+// time := hdr.ExpiresAt().Get()
//
//Don't worry about the missing `err` in the last line. When the header fails
//to parse, Object.Headers() already returns the corresponding
//MalformedHeaderError.
type FieldUnixTime struct {
- h headerInterface
+ h Headers
k string
}
@@ -151,7 +151,7 @@ func (f FieldUnixTime) validate() error {
//FieldUnixTimeReadonly is a readonly variant of FieldUnixTime. It is used for
//fields that cannot be set by the client.
type FieldUnixTimeReadonly struct {
- h headerInterface
+ h Headers
k string
}
diff --git a/field_uint64.go b/field_uint64.go
index a14f558..2a542ae 100644
--- a/field_uint64.go
+++ b/field_uint64.go
@@ -26,12 +26,12 @@ import (
//whose value is an unsigned integer. It cannot be directly constructed, but
//methods on the Headers types return this type. For example:
//
-// hdr := make(AccountHeaders)
-// //the following two statements are equivalent:
-// hdr["X-Account-Meta-Quota-Bytes"] = "1048576"
-// hdr.QuotaBytes().Set(1 << 20)
+// hdr := NewAccountHeaders()
+// //the following two statements are equivalent:
+// hdr["X-Account-Meta-Quota-Bytes"] = "1048576"
+// hdr.BytesUsedQuota().Set(1 << 20)
type FieldUint64 struct {
- h headerInterface
+ h Headers
k string
}
@@ -85,7 +85,7 @@ func (f FieldUint64) validate() error {
//FieldUint64Readonly is a readonly variant of FieldUint64. It is used for
//fields that cannot be set by the client.
type FieldUint64Readonly struct {
- h headerInterface
+ h Headers
k string
}
diff --git a/generated.go b/generated.go
index 2c109c1..abc4f90 100644
--- a/generated.go
+++ b/generated.go
@@ -8,38 +8,18 @@
package schwift
-import "net/textproto"
-
//AccountHeaders contains the headers for a schwift.Account instance.
//
//To read and write well-known headers, use the methods on this type.
-//To read and write arbitary headers, use the http.Header-like methods Get(),
-//Set(), Clear(), Del().
-type AccountHeaders map[string]string
-
-//Clear sets the value for the specified header to the empty string. When the
-//Headers instance is then sent to the server with Update(), the server will
-//delete the value for that header; cf. Del().
-func (h AccountHeaders) Clear(key string) {
- h[textproto.CanonicalMIMEHeaderKey(key)] = ""
+//To read and write arbitary headers, use the methods on the Headers supertype.
+type AccountHeaders struct {
+ Headers
}
-//Del deletes a key from the Headers instance. When the Headers instance is
-//then sent to the server with Update(), a key which has been deleted with
-//Del() will remain unchanged on the server.
-func (h AccountHeaders) Del(key string) {
- delete(h, textproto.CanonicalMIMEHeaderKey(key))
-}
-
-//Get returns the value for the specified header.
-func (h AccountHeaders) Get(key string) string {
- return h[textproto.CanonicalMIMEHeaderKey(key)]
-}
-
-//Set sets a new value for the specified header. Any existing value will be
-//overwritten.
-func (h AccountHeaders) Set(key, value string) {
- h[textproto.CanonicalMIMEHeaderKey(key)] = value
+//NewAccountHeaders creates a new AccountHeaders instance. The return value
+//will have the Headers attribute initialized to a non-nil map.
+func NewAccountHeaders() AccountHeaders {
+ return AccountHeaders{make(Headers)}
}
//Validate returns MalformedHeaderError if the value of any well-known header
@@ -77,74 +57,56 @@ func (h AccountHeaders) Validate() error {
//BytesUsed provides type-safe access to X-Account-Bytes-Used headers.
func (h AccountHeaders) BytesUsed() FieldUint64Readonly {
- return FieldUint64Readonly{h, "X-Account-Bytes-Used"}
+ return FieldUint64Readonly{h.Headers, "X-Account-Bytes-Used"}
}
//ContainerCount provides type-safe access to X-Account-Container-Count headers.
func (h AccountHeaders) ContainerCount() FieldUint64Readonly {
- return FieldUint64Readonly{h, "X-Account-Container-Count"}
+ return FieldUint64Readonly{h.Headers, "X-Account-Container-Count"}
}
//Metadata provides type-safe access to X-Account-Meta- headers.
func (h AccountHeaders) Metadata() FieldMetadata {
- return FieldMetadata{h, "X-Account-Meta-"}
+ return FieldMetadata{h.Headers, "X-Account-Meta-"}
}
//BytesUsedQuota provides type-safe access to X-Account-Meta-Quota-Bytes headers.
func (h AccountHeaders) BytesUsedQuota() FieldUint64 {
- return FieldUint64{h, "X-Account-Meta-Quota-Bytes"}
+ return FieldUint64{h.Headers, "X-Account-Meta-Quota-Bytes"}
}
//TempURLKey2 provides type-safe access to X-Account-Meta-Temp-URL-Key-2 headers.
func (h AccountHeaders) TempURLKey2() FieldString {
- return FieldString{h, "X-Account-Meta-Temp-URL-Key-2"}
+ return FieldString{h.Headers, "X-Account-Meta-Temp-URL-Key-2"}
}
//TempURLKey provides type-safe access to X-Account-Meta-Temp-URL-Key headers.
func (h AccountHeaders) TempURLKey() FieldString {
- return FieldString{h, "X-Account-Meta-Temp-URL-Key"}
+ return FieldString{h.Headers, "X-Account-Meta-Temp-URL-Key"}
}
//ObjectCount provides type-safe access to X-Account-Object-Count headers.
func (h AccountHeaders) ObjectCount() FieldUint64Readonly {
- return FieldUint64Readonly{h, "X-Account-Object-Count"}
+ return FieldUint64Readonly{h.Headers, "X-Account-Object-Count"}
}
//CreatedAt provides type-safe access to X-Timestamp headers.
func (h AccountHeaders) CreatedAt() FieldUnixTimeReadonly {
- return FieldUnixTimeReadonly{h, "X-Timestamp"}
+ return FieldUnixTimeReadonly{h.Headers, "X-Timestamp"}
}
//ContainerHeaders contains the headers for a schwift.Container instance.
//
//To read and write well-known headers, use the methods on this type.
-//To read and write arbitary headers, use the http.Header-like methods Get(),
-//Set(), Clear(), Del().
-type ContainerHeaders map[string]string
-
-//Clear sets the value for the specified header to the empty string. When the
-//Headers instance is then sent to the server with Update(), the server will
-//delete the value for that header; cf. Del().
-func (h ContainerHeaders) Clear(key string) {
- h[textproto.CanonicalMIMEHeaderKey(key)] = ""
-}
-
-//Del deletes a key from the Headers instance. When the Headers instance is
-//then sent to the server with Update(), a key which has been deleted with
-//Del() will remain unchanged on the server.
-func (h ContainerHeaders) Del(key string) {
- delete(h, textproto.CanonicalMIMEHeaderKey(key))
+//To read and write arbitary headers, use the methods on the Headers supertype.
+type ContainerHeaders struct {
+ Headers
}
-//Get returns the value for the specified header.
-func (h ContainerHeaders) Get(key string) string {
- return h[textproto.CanonicalMIMEHeaderKey(key)]
-}
-
-//Set sets a new value for the specified header. Any existing value will be
-//overwritten.
-func (h ContainerHeaders) Set(key, value string) {
- h[textproto.CanonicalMIMEHeaderKey(key)] = value
+//NewContainerHeaders creates a new ContainerHeaders instance. The return value
+//will have the Headers attribute initialized to a non-nil map.
+func NewContainerHeaders() ContainerHeaders {
+ return ContainerHeaders{make(Headers)}
}
//Validate returns MalformedHeaderError if the value of any well-known header
@@ -203,117 +165,91 @@ func (h ContainerHeaders) Validate() error {
//BytesUsed provides type-safe access to X-Container-Bytes-Used headers.
func (h ContainerHeaders) BytesUsed() FieldUint64Readonly {
- return FieldUint64Readonly{h, "X-Container-Bytes-Used"}
+ return FieldUint64Readonly{h.Headers, "X-Container-Bytes-Used"}
}
//Metadata provides type-safe access to X-Container-Meta- headers.
func (h ContainerHeaders) Metadata() FieldMetadata {
- return FieldMetadata{h, "X-Container-Meta-"}
+ return FieldMetadata{h.Headers, "X-Container-Meta-"}
}
//BytesUsedQuota provides type-safe access to X-Container-Meta-Quota-Bytes headers.
func (h ContainerHeaders) BytesUsedQuota() FieldUint64 {
- return FieldUint64{h, "X-Container-Meta-Quota-Bytes"}
+ return FieldUint64{h.Headers, "X-Container-Meta-Quota-Bytes"}
}
//ObjectCountQuota provides type-safe access to X-Container-Meta-Quota-Count headers.
func (h ContainerHeaders) ObjectCountQuota() FieldUint64 {
- return FieldUint64{h, "X-Container-Meta-Quota-Count"}
+ return FieldUint64{h.Headers, "X-Container-Meta-Quota-Count"}
}
//TempURLKey2 provides type-safe access to X-Container-Meta-Temp-URL-Key-2 headers.
func (h ContainerHeaders) TempURLKey2() FieldString {
- return FieldString{h, "X-Container-Meta-Temp-URL-Key-2"}
+ return FieldString{h.Headers, "X-Container-Meta-Temp-URL-Key-2"}
}
//TempURLKey provides type-safe access to X-Container-Meta-Temp-URL-Key headers.
func (h ContainerHeaders) TempURLKey() FieldString {
- return FieldString{h, "X-Container-Meta-Temp-URL-Key"}
+ return FieldString{h.Headers, "X-Container-Meta-Temp-URL-Key"}
}
//ObjectCount provides type-safe access to X-Container-Object-Count headers.
func (h ContainerHeaders) ObjectCount() FieldUint64Readonly {
- return FieldUint64Readonly{h, "X-Container-Object-Count"}
+ return FieldUint64Readonly{h.Headers, "X-Container-Object-Count"}
}
//ReadACL provides type-safe access to X-Container-Read headers.
func (h ContainerHeaders) ReadACL() FieldString {
- return FieldString{h, "X-Container-Read"}
+ return FieldString{h.Headers, "X-Container-Read"}
}
//SyncKey provides type-safe access to X-Container-Sync-Key headers.
func (h ContainerHeaders) SyncKey() FieldString {
- return FieldString{h, "X-Container-Sync-Key"}
+ return FieldString{h.Headers, "X-Container-Sync-Key"}
}
//SyncTo provides type-safe access to X-Container-Sync-To headers.
func (h ContainerHeaders) SyncTo() FieldString {
- return FieldString{h, "X-Container-Sync-To"}
+ return FieldString{h.Headers, "X-Container-Sync-To"}
}
//WriteACL provides type-safe access to X-Container-Write headers.
func (h ContainerHeaders) WriteACL() FieldString {
- return FieldString{h, "X-Container-Write"}
+ return FieldString{h.Headers, "X-Container-Write"}
}
//HistoryLocation provides type-safe access to X-History-Location headers.
func (h ContainerHeaders) HistoryLocation() FieldString {
- return FieldString{h, "X-History-Location"}
+ return FieldString{h.Headers, "X-History-Location"}
}
//StoragePolicy provides type-safe access to X-Storage-Policy headers.
func (h ContainerHeaders) StoragePolicy() FieldString {
- return FieldString{h, "X-Storage-Policy"}
+ return FieldString{h.Headers, "X-Storage-Policy"}
}
//CreatedAt provides type-safe access to X-Timestamp headers.
func (h ContainerHeaders) CreatedAt() FieldUnixTimeReadonly {
- return FieldUnixTimeReadonly{h, "X-Timestamp"}
+ return FieldUnixTimeReadonly{h.Headers, "X-Timestamp"}
}
//VersionsLocation provides type-safe access to X-Versions-Location headers.
func (h ContainerHeaders) VersionsLocation() FieldString {
- return FieldString{h, "X-Versions-Location"}
+ return FieldString{h.Headers, "X-Versions-Location"}
}
//ObjectHeaders contains the headers for a schwift.Object instance.
//
//To read and write well-known headers, use the methods on this type.
-//To read and write arbitary headers, use the http.Header-like methods Get(),
-//Set(), Clear(), Del().
-type ObjectHeaders map[string]string
-
-//Clear sets the value for the specified header to the empty string. When the
-//Headers instance is then sent to the server with Update(), the server will
-//delete the value for that header; cf. Del().
-func (h ObjectHeaders) Clear(key string) {
- h[textproto.CanonicalMIMEHeaderKey(key)] = ""
-}
-
-//Del deletes a key from the Headers instance. When the Headers instance
-//is then sent to the server with Update(), Del() has different effects
-//depending on context because of Swift's inconsistent API:
-//
-//For most writable attributes, a key which has been deleted with Del() will
-//remain unchanged on the server. To remove the key on the server, use Clear()
-//instead.
-//
-//For object metadata (but not other object attributes), deleting a key will
-//cause that key to be deleted on the server. Del() is identical to Clear() in
-//this case.
-func (h ObjectHeaders) Del(key string) {
- delete(h, textproto.CanonicalMIMEHeaderKey(key))
-}
-
-//Get returns the value for the specified header.
-func (h ObjectHeaders) Get(key string) string {
- return h[textproto.CanonicalMIMEHeaderKey(key)]
+//To read and write arbitary headers, use the methods on the Headers supertype.
+type ObjectHeaders struct {
+ Headers
}
-//Set sets a new value for the specified header. Any existing value will be
-//overwritten.
-func (h ObjectHeaders) Set(key, value string) {
- h[textproto.CanonicalMIMEHeaderKey(key)] = value
+//NewObjectHeaders creates a new ObjectHeaders instance. The return value
+//will have the Headers attribute initialized to a non-nil map.
+func NewObjectHeaders() ObjectHeaders {
+ return ObjectHeaders{make(Headers)}
}
//Validate returns MalformedHeaderError if the value of any well-known header
@@ -360,57 +296,57 @@ func (h ObjectHeaders) Validate() error {
//ContentDisposition provides type-safe access to Content-Disposition headers.
func (h ObjectHeaders) ContentDisposition() FieldString {
- return FieldString{h, "Content-Disposition"}
+ return FieldString{h.Headers, "Content-Disposition"}
}
//ContentEncoding provides type-safe access to Content-Encoding headers.
func (h ObjectHeaders) ContentEncoding() FieldString {
- return FieldString{h, "Content-Encoding"}
+ return FieldString{h.Headers, "Content-Encoding"}
}
//SizeBytes provides type-safe access to Content-Length headers.
func (h ObjectHeaders) SizeBytes() FieldUint64 {
- return FieldUint64{h, "Content-Length"}
+ return FieldUint64{h.Headers, "Content-Length"}
}
//ContentType provides type-safe access to Content-Type headers.
func (h ObjectHeaders) ContentType() FieldString {
- return FieldString{h, "Content-Type"}
+ return FieldString{h.Headers, "Content-Type"}
}
//Etag provides type-safe access to Etag headers.
func (h ObjectHeaders) Etag() FieldString {
- return FieldString{h, "Etag"}
+ return FieldString{h.Headers, "Etag"}
}
//UpdatedAt provides type-safe access to Last-Modified headers.
func (h ObjectHeaders) UpdatedAt() FieldHTTPTimeReadonly {
- return FieldHTTPTimeReadonly{h, "Last-Modified"}
+ return FieldHTTPTimeReadonly{h.Headers, "Last-Modified"}
}
//ExpiresAt provides type-safe access to X-Delete-At headers.
func (h ObjectHeaders) ExpiresAt() FieldUnixTime {
- return FieldUnixTime{h, "X-Delete-At"}
+ return FieldUnixTime{h.Headers, "X-Delete-At"}
}
//Metadata provides type-safe access to X-Object-Meta- headers.
func (h ObjectHeaders) Metadata() FieldMetadata {
- return FieldMetadata{h, "X-Object-Meta-"}
+ return FieldMetadata{h.Headers, "X-Object-Meta-"}
}
//SymlinkTargetAccount provides type-safe access to X-Symlink-Target-Account headers.
func (h ObjectHeaders) SymlinkTargetAccount() FieldString {
- return FieldString{h, "X-Symlink-Target-Account"}
+ return FieldString{h.Headers, "X-Symlink-Target-Account"}
}
//SymlinkTarget provides type-safe access to X-Symlink-Target headers.
func (h ObjectHeaders) SymlinkTarget() FieldString {
- return FieldString{h, "X-Symlink-Target"}
+ return FieldString{h.Headers, "X-Symlink-Target"}
}
//CreatedAt provides type-safe access to X-Timestamp headers.
func (h ObjectHeaders) CreatedAt() FieldUnixTimeReadonly {
- return FieldUnixTimeReadonly{h, "X-Timestamp"}
+ return FieldUnixTimeReadonly{h.Headers, "X-Timestamp"}
}
func evadeGolintComplaint1() error {
diff --git a/generated.go.in b/generated.go.in
index 85c257a..2858bcb 100644
--- a/generated.go.in
+++ b/generated.go.in
@@ -57,54 +57,20 @@
package schwift
-import "net/textproto"
-
{{- range $htype, $hmeta := . }}
//{{$htype}}Headers contains the headers for a schwift.{{$htype}} instance.
//
//To read and write well-known headers, use the methods on this type.
-//To read and write arbitary headers, use the http.Header-like methods Get(),
-//Set(), Clear(), Del().
-type {{$htype}}Headers map[string]string
-
-//Clear sets the value for the specified header to the empty string. When the
-//Headers instance is then sent to the server with Update(), the server will
-//delete the value for that header; cf. Del().
-func (h {{$htype}}Headers) Clear(key string) {
- h[textproto.CanonicalMIMEHeaderKey(key)] = ""
-}
-{{/* */}}
-{{- if eq $htype "Object" }}
-//Del deletes a key from the Headers instance. When the Headers instance
-//is then sent to the server with Update(), Del() has different effects
-//depending on context because of Swift's inconsistent API:
-//
-//For most writable attributes, a key which has been deleted with Del() will
-//remain unchanged on the server. To remove the key on the server, use Clear()
-//instead.
-//
-//For object metadata (but not other object attributes), deleting a key will
-//cause that key to be deleted on the server. Del() is identical to Clear() in
-//this case.
-{{- else }}
-//Del deletes a key from the Headers instance. When the Headers instance is
-//then sent to the server with Update(), a key which has been deleted with
-//Del() will remain unchanged on the server.
-{{- end }}
-func (h {{$htype}}Headers) Del(key string) {
- delete(h, textproto.CanonicalMIMEHeaderKey(key))
-}
-
-//Get returns the value for the specified header.
-func (h {{$htype}}Headers) Get(key string) string {
- return h[textproto.CanonicalMIMEHeaderKey(key)]
+//To read and write arbitary headers, use the methods on the Headers supertype.
+type {{$htype}}Headers struct {
+ Headers
}
-//Set sets a new value for the specified header. Any existing value will be
-//overwritten.
-func (h {{$htype}}Headers) Set(key, value string) {
- h[textproto.CanonicalMIMEHeaderKey(key)] = value
+//New{{$htype}}Headers creates a new {{$htype}}Headers instance. The return value
+//will have the Headers attribute initialized to a non-nil map.
+func New{{$htype}}Headers() {{$htype}}Headers {
+ return {{$htype}}Headers{make(Headers)}
}
//Validate returns MalformedHeaderError if the value of any well-known header
@@ -125,7 +91,7 @@ func (h {{$htype}}Headers) Validate() error {
//{{$field.Attribute}} provides type-safe access to {{$field.Header}} headers.
func (h {{$htype}}Headers) {{$field.Attribute}}() Field{{$field.Type}} {
- return Field{{$field.Type}}{h, "{{$field.Header}}"}
+ return Field{{$field.Type}}{h.Headers, "{{$field.Header}}"}
}
{{- end }}
{{- end }}
diff --git a/headers.go b/headers.go
index 7a6ec16..88c1a63 100644
--- a/headers.go
+++ b/headers.go
@@ -23,7 +23,56 @@ import (
"net/textproto"
)
-func headersToHTTP(h map[string]string) http.Header {
+//Headers represents a set of request headers or response headers.
+//
+//Users will typically use one of the subtypes (AccountHeaders,
+//ContainerHeaders, ObjectHeaders) instead, which provide type-safe access to
+//well-known headers. The http.Header-like interface on this type can be used
+//read and write arbitary headers. For example, the following calls are
+//equivalent:
+//
+// h := make(AccountHeaders)
+// h.Headers.Set("X-Account-Meta-Quota-Bytes", "1048576")
+// h.BytesUsedQuota().Set(1048576)
+//
+type Headers map[string]string
+
+//Clear sets the value for the specified header to the empty string. When the
+//Headers instance is then sent to the server with Update(), the server will
+//delete the value for that header; cf. Del().
+func (h Headers) Clear(key string) {
+ h[textproto.CanonicalMIMEHeaderKey(key)] = ""
+}
+
+//Del deletes a key from the Headers instance. When the Headers instance is
+//then sent to the server with Update(), a key which has been deleted with
+//Del() will remain unchanged on the server.
+//
+//For most writable attributes, a key which has been deleted with Del() will
+//remain unchanged on the server. To remove the key on the server, use Clear()
+//instead.
+//
+//For object metadata (but not other object attributes), deleting a key will
+//cause that key to be deleted on the server. Del() is identical to Clear() in
+//this case.
+func (h Headers) Del(key string) {
+ delete(h, textproto.CanonicalMIMEHeaderKey(key))
+}
+
+//Get returns the value for the specified header.
+func (h Headers) Get(key string) string {
+ return h[textproto.CanonicalMIMEHeaderKey(key)]
+}
+
+//Set sets a new value for the specified header. Any existing value will be
+//overwritten.
+func (h Headers) Set(key, value string) {
+ h[textproto.CanonicalMIMEHeaderKey(key)] = value
+}
+
+//ToHTTP converts this Headers instance into the equivalent http.Header
+//instance. The return value is guaranteed to be non-nil.
+func (h Headers) ToHTTP() http.Header {
dest := make(http.Header, len(h))
for k, v := range h {
dest.Set(k, v)
@@ -31,19 +80,24 @@ func headersToHTTP(h map[string]string) http.Header {
return dest
}
-func headersFromHTTP(src http.Header) map[string]string {
- h := make(map[string]string, len(src))
+//ToOpts wraps this Headers instance into a RequestOpts instance, so that it
+//can be passed to Schwift's various request methods.
+//
+// hdr := NewObjectHeaders()
+// hdr.ContentType().Set("image/png")
+// hdr.Metadata().Set("color", "blue")
+// obj.Upload(content, hdr.ToOpts())
+//
+func (h Headers) ToOpts() *RequestOptions {
+ return &RequestOptions{Headers: h}
+}
+
+func headersFromHTTP(src http.Header) Headers {
+ h := make(Headers, len(src))
for k, v := range src {
if len(v) > 0 {
- h[textproto.CanonicalMIMEHeaderKey(k)] = v[0]
+ h.Set(k, v[0])
}
}
return h
}
-
-type headerInterface interface {
- Clear(string)
- Del(string)
- Get(string) string
- Set(string, string)
-}
diff --git a/iterator.go b/iterator.go
index e460edd..04fb492 100644
--- a/iterator.go
+++ b/iterator.go
@@ -31,21 +31,19 @@ type iteratorInterface interface {
getAccount() *Account
getContainerName() string
getPrefix() string
- getHeaders() map[string]string
getOptions() *RequestOptions
//putHeader initializes the AccountHeaders/ContainerHeaders field of the
//Account/Container using the response headers from the GET request.
putHeader(http.Header) error
}
-func (i ContainerIterator) getAccount() *Account { return i.Account }
-func (i ContainerIterator) getContainerName() string { return "" }
-func (i ContainerIterator) getPrefix() string { return i.Prefix }
-func (i ContainerIterator) getHeaders() map[string]string { return i.Headers }
-func (i ContainerIterator) getOptions() *RequestOptions { return i.Options }
+func (i ContainerIterator) getAccount() *Account { return i.Account }
+func (i ContainerIterator) getContainerName() string { return "" }
+func (i ContainerIterator) getPrefix() string { return i.Prefix }
+func (i ContainerIterator) getOptions() *RequestOptions { return i.Options }
func (i ContainerIterator) putHeader(hdr http.Header) error {
- headers := AccountHeaders(headersFromHTTP(hdr))
+ headers := AccountHeaders{headersFromHTTP(hdr)}
if err := headers.Validate(); err != nil {
return err
}
@@ -53,14 +51,13 @@ func (i ContainerIterator) putHeader(hdr http.Header) error {
return nil
}
-func (i ObjectIterator) getAccount() *Account { return i.Container.Account() }
-func (i ObjectIterator) getContainerName() string { return i.Container.Name() }
-func (i ObjectIterator) getPrefix() string { return i.Prefix }
-func (i ObjectIterator) getHeaders() map[string]string { return i.Headers }
-func (i ObjectIterator) getOptions() *RequestOptions { return i.Options }
+func (i ObjectIterator) getAccount() *Account { return i.Container.Account() }
+func (i ObjectIterator) getContainerName() string { return i.Container.Name() }
+func (i ObjectIterator) getPrefix() string { return i.Prefix }
+func (i ObjectIterator) getOptions() *RequestOptions { return i.Options }
func (i ObjectIterator) putHeader(hdr http.Header) error {
- headers := ContainerHeaders(headersFromHTTP(hdr))
+ headers := ContainerHeaders{headersFromHTTP(hdr)}
if err := headers.Validate(); err != nil {
return err
}
@@ -79,8 +76,7 @@ func (b *iteratorBase) request(limit int, detailed bool) Request {
r := Request{
Method: "GET",
ContainerName: b.i.getContainerName(),
- Headers: headersToHTTP(b.i.getHeaders()),
- Options: cloneRequestOptions(b.i.getOptions()),
+ Options: cloneRequestOptions(b.i.getOptions(), nil),
}
if prefix := b.i.getPrefix(); prefix != "" {
@@ -100,11 +96,11 @@ func (b *iteratorBase) request(limit int, detailed bool) Request {
}
if detailed {
- r.Headers.Set("Accept", "application/json")
+ r.Options.Headers.Set("Accept", "application/json")
r.Options.Values.Set("format", "json")
r.ExpectStatusCodes = []int{200}
} else {
- r.Headers.Set("Accept", "text/plain")
+ r.Options.Headers.Set("Accept", "text/plain")
r.Options.Values.Set("format", "plain")
r.ExpectStatusCodes = []int{200, 204}
}
diff --git a/object.go b/object.go
index 4af37e6..fb72aeb 100644
--- a/object.go
+++ b/object.go
@@ -104,7 +104,7 @@ func (o *Object) Headers() (ObjectHeaders, error) {
return ObjectHeaders{}, err
}
- headers := ObjectHeaders(headersFromHTTP(resp.Header))
+ headers := ObjectHeaders{headersFromHTTP(resp.Header)}
err = headers.Validate()
if err != nil {
return headers, err
@@ -124,8 +124,7 @@ func (o *Object) Update(headers ObjectHeaders, opts *RequestOptions) error {
Method: "POST",
ContainerName: o.c.name,
ObjectName: o.name,
- Headers: headersToHTTP(headers),
- Options: opts,
+ Options: cloneRequestOptions(opts, headers.Headers),
ExpectStatusCodes: []int{202},
}.Do(o.c.a.backend)
if err == nil {
@@ -134,18 +133,17 @@ func (o *Object) Update(headers ObjectHeaders, opts *RequestOptions) error {
return err
}
-//Upload creates the object using a PUT request. To add URL parameters, pass
-//a non-nil *RequestOptions.
+//Upload creates the object using a PUT request.
//
//If you do not have an io.Reader, but you have a []byte or string instance
//containing the object, wrap it in a *bytes.Reader instance like so:
//
// var buffer []byte
-// o.Upload(bytes.NewReader(buffer), headers, opts)
+// o.Upload(bytes.NewReader(buffer), opts)
//
// //or...
// var buffer string
-// o.Upload(bytes.NewReader([]byte(buffer)), headers, opts)
+// o.Upload(bytes.NewReader([]byte(buffer)), opts)
//
//If you have neither an io.Reader nor a []byte or string, but you have a
//function that generates the object's content into an io.Writer, use
@@ -166,16 +164,15 @@ func (o *Object) Update(headers ObjectHeaders, opts *RequestOptions) error {
//This function can be used regardless of whether the object exists or not.
//
//A successful PUT request implies Invalidate() since it may change metadata.
-func (o *Object) Upload(content io.Reader, headers ObjectHeaders, opts *RequestOptions) error {
- if headers == nil {
- headers = make(ObjectHeaders)
- }
- tryComputeContentLength(content, headers)
- tryComputeEtag(content, headers)
+func (o *Object) Upload(content io.Reader, opts *RequestOptions) error {
+ opts = cloneRequestOptions(opts, nil)
+ hdr := ObjectHeaders{opts.Headers}
+ tryComputeContentLength(content, hdr)
+ tryComputeEtag(content, hdr)
//could not compute Etag in advance -> need to check on the fly
var hasher hash.Hash
- if !headers.Etag().Exists() {
+ if !hdr.Etag().Exists() {
hasher = md5.New()
if content != nil {
content = io.TeeReader(content, hasher)
@@ -186,7 +183,6 @@ func (o *Object) Upload(content io.Reader, headers ObjectHeaders, opts *RequestO
Method: "PUT",
ContainerName: o.c.name,
ObjectName: o.name,
- Headers: headersToHTTP(headers),
Options: opts,
Body: content,
ExpectStatusCodes: []int{201},
@@ -253,7 +249,7 @@ func tryComputeEtag(content io.Reader, headers ObjectHeaders) {
// }
//
// obj := container.Object("greeting-for-susan-and-jeffrey")
-// err := obj.UploadWithWriter(nil, nil, func(w io.Writer) error {
+// err := obj.UploadWithWriter(nil, func(w io.Writer) error {
// err := greeting(w, "Susan")
// if err == nil {
// err = greeting(w, "Jeffrey")
@@ -262,11 +258,11 @@ func tryComputeEtag(content io.Reader, headers ObjectHeaders) {
// })
//
//If you do not need an io.Writer, always use Upload instead.
-func (o *Object) UploadWithWriter(headers ObjectHeaders, opts *RequestOptions, callback func(io.Writer) error) error {
+func (o *Object) UploadWithWriter(opts *RequestOptions, callback func(io.Writer) error) error {
reader, writer := io.Pipe()
errChan := make(chan error)
go func() {
- err := o.Upload(reader, headers, opts)
+ err := o.Upload(reader, opts)
reader.CloseWithError(err) //stop the writer if it is still writing
errChan <- err
}()
@@ -280,12 +276,11 @@ func (o *Object) UploadWithWriter(headers ObjectHeaders, opts *RequestOptions, c
//This operation fails with http.StatusNotFound if the object does not exist.
//
//A successful DELETE request implies Invalidate().
-func (o *Object) Delete(headers ObjectHeaders, opts *RequestOptions) error {
+func (o *Object) Delete(opts *RequestOptions) error {
_, err := Request{
Method: "DELETE",
ContainerName: o.c.name,
ObjectName: o.name,
- Headers: headersToHTTP(headers),
Options: opts,
ExpectStatusCodes: []int{204},
}.Do(o.c.a.backend)
@@ -313,18 +308,17 @@ func (o *Object) Invalidate() {
// str, err := object.Download(nil, nil).AsString()
//
//See struct DownloadedObject for details.
-func (o *Object) Download(headers ObjectHeaders, opts *RequestOptions) DownloadedObject {
+func (o *Object) Download(opts *RequestOptions) DownloadedObject {
resp, err := Request{
Method: "GET",
ContainerName: o.c.name,
ObjectName: o.name,
- Headers: headersToHTTP(headers),
Options: opts,
ExpectStatusCodes: []int{200},
}.Do(o.c.a.backend)
var body io.ReadCloser
if err == nil {
- newHeaders := ObjectHeaders(headersFromHTTP(resp.Header))
+ newHeaders := ObjectHeaders{headersFromHTTP(resp.Header)}
err = newHeaders.Validate()
if err == nil {
o.headers = &newHeaders
@@ -347,18 +341,17 @@ func (o *Object) Download(headers ObjectHeaders, opts *RequestOptions) Downloade
//
//A successful COPY implies target.Invalidate() since it may change the
//target's metadata.
-func (o *Object) CopyTo(target *Object, headers ObjectHeaders, opts *RequestOptions) error {
- hdr := headersToHTTP(headers)
- hdr.Set("Destination", target.FullName())
+func (o *Object) CopyTo(target *Object, opts *RequestOptions) error {
+ opts = cloneRequestOptions(opts, nil)
+ opts.Headers.Set("Destination", target.FullName())
if o.c.a.name != target.c.a.name {
- hdr.Set("Destination-Account", target.c.a.name)
+ opts.Headers.Set("Destination-Account", target.c.a.name)
}
_, err := Request{
Method: "COPY",
ContainerName: o.c.name,
ObjectName: o.name,
- Headers: hdr,
Options: opts,
ExpectStatusCodes: []int{201},
DrainResponseBody: true,
@@ -373,10 +366,10 @@ func (o *Object) CopyTo(target *Object, headers ObjectHeaders, opts *RequestOpti
//a DELETE request on the source object.
//
//A successful move implies Invalidate() on both the source and target side.
-func (o *Object) MoveTo(target *Object, headers ObjectHeaders, opts *RequestOptions) error {
- err := o.CopyTo(target, headers, opts)
+func (o *Object) MoveTo(target *Object, copyOpts *RequestOptions, deleteOpts *RequestOptions) error {
+ err := o.CopyTo(target, copyOpts)
if err != nil {
return err
}
- return o.Delete(nil, nil)
+ return o.Delete(deleteOpts)
}
diff --git a/object_iterator.go b/object_iterator.go
index 6d93ccf..3df03dd 100644
--- a/object_iterator.go
+++ b/object_iterator.go
@@ -65,12 +65,10 @@ type ObjectIterator struct {
//When Prefix is set, only objects whose name starts with this string are
//returned.
Prefix string
- //Headers may contain additional headers to include with the GET request.
- Headers map[string]string
- //Options may contain additional query parameters for the GET request.
+ //Options may contain additional headers and query parameters for the GET request.
Options *RequestOptions
- //TODO: Delimter field (and check if other stuff is missing)
+ //TODO: Delimiter field (and check if other stuff is missing)
base *iteratorBase
}
diff --git a/request.go b/request.go
index b96670b..3e089c0 100644
--- a/request.go
+++ b/request.go
@@ -26,20 +26,39 @@ import (
"strings"
)
-//RequestOptions contains additional headers and values for a request.
+//RequestOptions is used to pass additional headers and values to aa request.
+//
+//When preparing a RequestOptions instance with additional headers, the
+//preferred way is to create an AccountHeaders, ContainerHeaders and
+//ObjectHeaders instance and use the type-safe API on these types. Then use the
+//ToOpts() method on that instance. For example:
+//
+// hdr := NewObjectHeaders()
+// hdr.ContentType().Set("image/png")
+// hdr.Metadata().Set("color", "blue")
+// opts := hdr.ToOpts() //type *schwift.RequestOptions
+//
type RequestOptions struct {
- Values url.Values
+ Headers Headers
+ Values url.Values
}
-func cloneRequestOptions(orig *RequestOptions) *RequestOptions {
+func cloneRequestOptions(orig *RequestOptions, additional Headers) *RequestOptions {
result := RequestOptions{
- Values: make(url.Values),
+ Headers: make(Headers),
+ Values: make(url.Values),
}
if orig != nil {
+ for k, v := range orig.Headers {
+ result.Headers[k] = v
+ }
for k, v := range orig.Values {
result.Values[k] = v
}
}
+ for k, v := range additional {
+ result.Headers[k] = v
+ }
return &result
}
@@ -48,7 +67,6 @@ type Request struct {
Method string //"GET", "HEAD", "PUT", "POST" or "DELETE"
ContainerName string //empty for requests on accounts
ObjectName string //empty for requests on accounts/containers
- Headers http.Header
Options *RequestOptions
Body io.Reader
//ExpectStatusCodes can be left empty to disable this check, otherwise
@@ -102,8 +120,10 @@ func (r Request) Do(backend Backend) (*http.Response, error) {
return nil, err
}
- for k, v := range r.Headers {
- req.Header[k] = v
+ if r.Options != nil {
+ for k, v := range r.Options.Headers {
+ req.Header[k] = []string{v}
+ }
}
if r.Body != nil {
req.Header.Set("Expect", "100-continue")
diff --git a/tests/account_test.go b/tests/account_test.go
index 310d4ce..2885bfa 100644
--- a/tests/account_test.go
+++ b/tests/account_test.go
@@ -41,7 +41,7 @@ func TestAccountBasic(t *testing.T) {
func TestAccountMetadata(t *testing.T) {
testWithAccount(t, func(a *schwift.Account) {
//test creating some metadata
- hdr := make(schwift.AccountHeaders)
+ hdr := schwift.NewAccountHeaders()
hdr.Metadata().Set("schwift-test1", "first")
hdr.Metadata().Set("schwift-test2", "second")
err := a.Update(hdr, nil)
@@ -57,7 +57,7 @@ func TestAccountMetadata(t *testing.T) {
expectString(t, hdr.Metadata().Get("schwift-test2"), "second")
//test deleting some metadata
- hdr = make(schwift.AccountHeaders)
+ hdr = schwift.NewAccountHeaders()
hdr.Metadata().Clear("schwift-test1")
err = a.Update(hdr, nil)
if !expectSuccess(t, err) {
@@ -72,7 +72,7 @@ func TestAccountMetadata(t *testing.T) {
expectString(t, hdr.Metadata().Get("schwift-test2"), "second")
//test updating some metadata
- hdr = make(schwift.AccountHeaders)
+ hdr = schwift.NewAccountHeaders()
hdr.Metadata().Set("schwift-test1", "will not be set")
hdr.Metadata().Del("schwift-test1")
hdr.Metadata().Set("schwift-test2", "changed")
diff --git a/tests/bulk_delete_test.go b/tests/bulk_delete_test.go
index 52294ba..c498419 100644
--- a/tests/bulk_delete_test.go
+++ b/tests/bulk_delete_test.go
@@ -33,13 +33,13 @@ func TestBulkDeleteSuccess(t *testing.T) {
objs, err := createTestObjects(c)
expectSuccess(t, err)
- numDeleted, numNotFound, err := c.Account().BulkDelete(objs, nil, nil, nil)
+ numDeleted, numNotFound, err := c.Account().BulkDelete(objs, nil, nil)
expectSuccess(t, err)
expectInt(t, numDeleted, len(objs))
expectInt(t, numNotFound, 0)
expectContainerExistence(t, c, true)
- numDeleted, numNotFound, err = c.Account().BulkDelete(objs, nil, nil, nil)
+ numDeleted, numNotFound, err = c.Account().BulkDelete(objs, nil, nil)
expectSuccess(t, err)
expectInt(t, numDeleted, 0)
expectInt(t, numNotFound, len(objs))
@@ -49,7 +49,7 @@ func TestBulkDeleteSuccess(t *testing.T) {
expectSuccess(t, err)
cs := []*schwift.Container{c}
- numDeleted, numNotFound, err = c.Account().BulkDelete(objs, cs, nil, nil)
+ numDeleted, numNotFound, err = c.Account().BulkDelete(objs, cs, nil)
expectSuccess(t, err)
expectInt(t, numDeleted, len(objs)+1)
expectInt(t, numNotFound, 0)
@@ -65,7 +65,7 @@ func TestBulkDeleteError(t *testing.T) {
cs := []*schwift.Container{c}
//not deleting all objects should lead to 409 Conflict when deleting the Container
- numDeleted, numNotFound, err := c.Account().BulkDelete(objs, cs, nil, nil)
+ numDeleted, numNotFound, err := c.Account().BulkDelete(objs, cs, nil)
expectInt(t, numDeleted, len(objs))
expectInt(t, numNotFound, 0)
expectError(t, err, "400 Bad Request (+1 object errors)")
@@ -77,7 +77,7 @@ func createTestObjects(c *schwift.Container) ([]*schwift.Object, error) {
var objs []*schwift.Object
for idx := 1; idx <= 5; idx++ {
obj := c.Object(fmt.Sprintf("object%d", idx))
- err := obj.Upload(strings.NewReader("example"), nil, nil)
+ err := obj.Upload(strings.NewReader("example"), nil)
if err != nil {
return nil, err
}
diff --git a/tests/bulk_upload_test.go b/tests/bulk_upload_test.go
index 68cb791..6cbae42 100644
--- a/tests/bulk_upload_test.go
+++ b/tests/bulk_upload_test.go
@@ -40,7 +40,7 @@ func TestBulkUploadSuccess(t *testing.T) {
"", //upload path
schwift.BulkUploadTar,
bytes.NewReader(archive),
- nil, nil,
+ nil,
)
expectInt(t, n, 2)
expectSuccess(t, err)
@@ -58,7 +58,7 @@ func TestBulkUploadArchiveError(t *testing.T) {
c.Name(), //upload path
schwift.BulkUploadTar,
strings.NewReader("This is not the TAR archive you're looking for."),
- nil, nil,
+ nil,
)
expectInt(t, n, 0)
expectError(t, err, "400 Bad Request: Invalid Tar File: truncated header")
@@ -83,7 +83,7 @@ func TestBulkUploadObjectError(t *testing.T) {
c.Name(), //upload path
schwift.BulkUploadTar,
bytes.NewReader(archive),
- nil, nil,
+ nil,
)
expectInt(t, n, 1)
expectError(t, err, "400 Bad Request (+1 object errors)")
diff --git a/tests/container_iterator_test.go b/tests/container_iterator_test.go
index b9813db..d682a59 100644
--- a/tests/container_iterator_test.go
+++ b/tests/container_iterator_test.go
@@ -126,7 +126,7 @@ func TestContainerIterator(t *testing.T) {
iter = a.Containers()
iter.Prefix = "schwift-test-listing"
expectSuccess(t, iter.Foreach(func(c *schwift.Container) error {
- return c.Delete(nil, nil)
+ return c.Delete(nil)
}))
})
}
diff --git a/tests/container_test.go b/tests/container_test.go
index c90a339..0efc4e6 100644
--- a/tests/container_test.go
+++ b/tests/container_test.go
@@ -46,17 +46,17 @@ func TestContainerLifecycle(t *testing.T) {
//DELETE should be idempotent and not return success on non-existence, but
//OpenStack LOVES to be inconsistent with everything (including, notably, itself)
- err = c.Delete(nil, nil)
+ err = c.Delete(nil)
expectError(t, err, "expected 204 response, got 404 instead: <html><h1>Not Found</h1><p>The resource could not be found.</p></html>")
- err = c.Create(nil, nil)
+ err = c.Create(nil)
expectSuccess(t, err)
exists, err = c.Exists()
expectSuccess(t, err)
expectBool(t, exists, true)
- err = c.Delete(nil, nil)
+ err = c.Delete(nil)
expectSuccess(t, err)
})
}
@@ -69,7 +69,7 @@ func TestContainerUpdate(t *testing.T) {
expectBool(t, hdr.ObjectCount().Exists(), true)
expectUint64(t, hdr.ObjectCount().Get(), 0)
- hdr = make(schwift.ContainerHeaders)
+ hdr = schwift.NewContainerHeaders()
hdr.ObjectCountQuota().Set(23)
hdr.BytesUsedQuota().Set(42)
diff --git a/tests/field_test.go b/tests/field_test.go
index f3c03ca..8166f2d 100644
--- a/tests/field_test.go
+++ b/tests/field_test.go
@@ -27,33 +27,33 @@ import (
)
func TestFieldString(t *testing.T) {
- hdr := make(schwift.AccountHeaders)
+ hdr := schwift.NewAccountHeaders()
expectBool(t, hdr.TempURLKey().Exists(), false)
expectString(t, hdr.TempURLKey().Get(), "")
expectSuccess(t, hdr.Validate())
- hdr["X-Account-Meta-Temp-Url-Key"] = ""
+ hdr.Headers["X-Account-Meta-Temp-Url-Key"] = ""
expectBool(t, hdr.TempURLKey().Exists(), false)
expectString(t, hdr.TempURLKey().Get(), "")
expectSuccess(t, hdr.Validate())
- hdr["X-Account-Meta-Temp-Url-Key"] = "foo"
+ hdr.Headers["X-Account-Meta-Temp-Url-Key"] = "foo"
expectBool(t, hdr.TempURLKey().Exists(), true)
expectString(t, hdr.TempURLKey().Get(), "foo")
expectSuccess(t, hdr.Validate())
hdr.TempURLKey().Set("bar")
- expectHeaders(t, hdr, map[string]string{
+ expectHeaders(t, hdr.Headers, map[string]string{
"X-Account-Meta-Temp-Url-Key": "bar",
})
hdr.TempURLKey().Clear()
- expectHeaders(t, hdr, map[string]string{
+ expectHeaders(t, hdr.Headers, map[string]string{
"X-Account-Meta-Temp-Url-Key": "",
})
hdr.TempURLKey().Del()
- expectHeaders(t, hdr, nil)
+ expectHeaders(t, hdr.Headers, nil)
hdr.TempURLKey().Clear()
- expectHeaders(t, hdr, map[string]string{
+ expectHeaders(t, hdr.Headers, map[string]string{
"X-Account-Meta-Temp-Url-Key": "",
})
}
@@ -70,16 +70,16 @@ func TestFieldTimestamp(t *testing.T) {
expectBool(t, hdr.CreatedAt().Exists(), true)
actual := float64(hdr.CreatedAt().Get().UnixNano()) / 1e9
- expected, _ := strconv.ParseFloat(hdr["X-Timestamp"], 64)
+ expected, _ := strconv.ParseFloat(hdr.Headers["X-Timestamp"], 64)
expectFloat64(t, actual, expected)
})
- hdr := make(schwift.AccountHeaders)
+ hdr := schwift.NewAccountHeaders()
expectBool(t, hdr.CreatedAt().Exists(), false)
expectBool(t, hdr.CreatedAt().Get().IsZero(), true)
expectSuccess(t, hdr.Validate())
- hdr["X-Timestamp"] = "wtf"
+ hdr.Headers["X-Timestamp"] = "wtf"
expectBool(t, hdr.CreatedAt().Exists(), true)
expectBool(t, hdr.CreatedAt().Get().IsZero(), true)
expectError(t, hdr.Validate(), `Bad header X-Timestamp: strconv.ParseFloat: parsing "wtf": invalid syntax`)
@@ -88,7 +88,7 @@ func TestFieldTimestamp(t *testing.T) {
func TestFieldHTTPTimestamp(t *testing.T) {
testWithContainer(t, func(c *schwift.Container) {
obj := c.Object("test")
- err := obj.Upload(nil, nil, nil)
+ err := obj.Upload(nil, nil)
if !expectSuccess(t, err) {
return
}
@@ -104,12 +104,12 @@ func TestFieldHTTPTimestamp(t *testing.T) {
expectInt64(t, actual.Unix(), expected.Unix())
})
- hdr := make(schwift.ObjectHeaders)
+ hdr := schwift.NewObjectHeaders()
expectBool(t, hdr.UpdatedAt().Exists(), false)
expectBool(t, hdr.UpdatedAt().Get().IsZero(), true)
expectSuccess(t, hdr.Validate())
- hdr["Last-Modified"] = "wtf"
+ hdr.Headers["Last-Modified"] = "wtf"
expectBool(t, hdr.UpdatedAt().Exists(), true)
expectBool(t, hdr.UpdatedAt().Get().IsZero(), true)
expectError(t, hdr.Validate(), `Bad header Last-Modified: parsing time "wtf" as "Mon Jan _2 15:04:05 2006": cannot parse "wtf" as "Mon"`)
@@ -118,49 +118,49 @@ func TestFieldHTTPTimestamp(t *testing.T) {
////////////////////////////////////////////////////////////////////////////////
func TestFieldUint64(t *testing.T) {
- hdr := make(schwift.AccountHeaders)
+ hdr := schwift.NewAccountHeaders()
expectBool(t, hdr.BytesUsedQuota().Exists(), false)
expectUint64(t, hdr.BytesUsedQuota().Get(), 0)
expectSuccess(t, hdr.Validate())
- hdr["X-Account-Meta-Quota-Bytes"] = "23"
+ hdr.Headers["X-Account-Meta-Quota-Bytes"] = "23"
expectBool(t, hdr.BytesUsedQuota().Exists(), true)
expectUint64(t, hdr.BytesUsedQuota().Get(), 23)
expectSuccess(t, hdr.Validate())
- hdr["X-Account-Meta-Quota-Bytes"] = "-23"
+ hdr.Headers["X-Account-Meta-Quota-Bytes"] = "-23"
expectBool(t, hdr.BytesUsedQuota().Exists(), true)
expectUint64(t, hdr.BytesUsedQuota().Get(), 0)
expectError(t, hdr.Validate(), `Bad header X-Account-Meta-Quota-Bytes: strconv.ParseUint: parsing "-23": invalid syntax`)
hdr.BytesUsedQuota().Set(9001)
- expectHeaders(t, hdr, map[string]string{
+ expectHeaders(t, hdr.Headers, map[string]string{
"X-Account-Meta-Quota-Bytes": "9001",
})
hdr.BytesUsedQuota().Clear()
- expectHeaders(t, hdr, map[string]string{
+ expectHeaders(t, hdr.Headers, map[string]string{
"X-Account-Meta-Quota-Bytes": "",
})
hdr.BytesUsedQuota().Del()
- expectHeaders(t, hdr, nil)
+ expectHeaders(t, hdr.Headers, nil)
hdr.BytesUsedQuota().Clear()
- expectHeaders(t, hdr, map[string]string{
+ expectHeaders(t, hdr.Headers, map[string]string{
"X-Account-Meta-Quota-Bytes": "",
})
}
func TestFieldUint64Readonly(t *testing.T) {
- hdr := make(schwift.AccountHeaders)
+ hdr := schwift.NewAccountHeaders()
expectBool(t, hdr.BytesUsed().Exists(), false)
expectUint64(t, hdr.BytesUsed().Get(), 0)
expectSuccess(t, hdr.Validate())
- hdr["X-Account-Bytes-Used"] = "23"
+ hdr.Headers["X-Account-Bytes-Used"] = "23"
expectBool(t, hdr.BytesUsed().Exists(), true)
expectUint64(t, hdr.BytesUsed().Get(), 23)
expectSuccess(t, hdr.Validate())
- hdr["X-Account-Bytes-Used"] = "-23"
+ hdr.Headers["X-Account-Bytes-Used"] = "-23"
expectBool(t, hdr.BytesUsed().Exists(), true)
expectUint64(t, hdr.BytesUsed().Get(), 0)
expectError(t, hdr.Validate(), `Bad header X-Account-Bytes-Used: strconv.ParseUint: parsing "-23": invalid syntax`)
diff --git a/tests/headers_test.go b/tests/headers_test.go
index 8067ef4..6b229ae 100644
--- a/tests/headers_test.go
+++ b/tests/headers_test.go
@@ -26,11 +26,13 @@ import (
func TestParseAccountHeadersSuccess(t *testing.T) {
headers := schwift.AccountHeaders{
- "X-Account-Bytes-Used": "1234",
- "X-Account-Object-Count": "42",
- "X-Account-Container-Count": "23",
- "X-Account-Meta-Quota-Bytes": "1048576",
- "X-Account-Meta-Foo": "bar",
+ Headers: schwift.Headers{
+ "X-Account-Bytes-Used": "1234",
+ "X-Account-Object-Count": "42",
+ "X-Account-Container-Count": "23",
+ "X-Account-Meta-Quota-Bytes": "1048576",
+ "X-Account-Meta-Foo": "bar",
+ },
}
expectSuccess(t, headers.Validate())
diff --git a/tests/object_iterator_test.go b/tests/object_iterator_test.go
index 5112324..009189d 100644
--- a/tests/object_iterator_test.go
+++ b/tests/object_iterator_test.go
@@ -37,9 +37,9 @@ func TestObjectIterator(t *testing.T) {
//create test objects that can be listed
for idx := 1; idx <= 4; idx++ {
- hdr := make(schwift.ObjectHeaders)
+ hdr := schwift.NewObjectHeaders()
hdr.ContentType().Set("application/json")
- err := c.Object(oname(idx)).Upload(bytes.NewReader(objectExampleContent), hdr, nil)
+ err := c.Object(oname(idx)).Upload(bytes.NewReader(objectExampleContent), hdr.ToOpts())
expectSuccess(t, err)
}
diff --git a/tests/object_test.go b/tests/object_test.go
index 090fc48..b42294a 100644
--- a/tests/object_test.go
+++ b/tests/object_test.go
@@ -47,15 +47,15 @@ func TestObjectLifecycle(t *testing.T) {
//DELETE should be idempotent and not return success on non-existence, but
//OpenStack LOVES to be inconsistent with everything (including, notably, itself)
- err = o.Delete(nil, nil)
+ err = o.Delete(nil)
expectError(t, err, "expected 204 response, got 404 instead: <html><h1>Not Found</h1><p>The resource could not be found.</p></html>")
- err = o.Upload(bytes.NewReader([]byte("test")), nil, nil)
+ err = o.Upload(bytes.NewReader([]byte("test")), nil)
expectSuccess(t, err)
expectObjectExistence(t, o, true)
- err = o.Delete(nil, nil)
+ err = o.Delete(nil)
expectSuccess(t, err)
})
}
@@ -65,25 +65,25 @@ func TestObjectUpload(t *testing.T) {
//test upload with bytes.Reader
obj := c.Object("upload1")
- err := obj.Upload(bytes.NewReader(objectExampleContent), nil, nil)
+ err := obj.Upload(bytes.NewReader(objectExampleContent), nil)
expectSuccess(t, err)
expectObjectContent(t, obj, objectExampleContent)
//test upload with bytes.Buffer
obj = c.Object("upload2")
- err = obj.Upload(bytes.NewBuffer(objectExampleContent), nil, nil)
+ err = obj.Upload(bytes.NewBuffer(objectExampleContent), nil)
expectSuccess(t, err)
expectObjectContent(t, obj, objectExampleContent)
//test upload with opaque io.Reader
obj = c.Object("upload3")
- err = obj.Upload(opaqueReader{bytes.NewReader(objectExampleContent)}, nil, nil)
+ err = obj.Upload(opaqueReader{bytes.NewReader(objectExampleContent)}, nil)
expectSuccess(t, err)
expectObjectContent(t, obj, objectExampleContent)
//test upload with io.Writer
obj = c.Object("upload4")
- err = obj.UploadWithWriter(nil, nil, func(w io.Writer) error {
+ err = obj.UploadWithWriter(nil, func(w io.Writer) error {
_, err := w.Write(objectExampleContent)
return err
})
@@ -92,13 +92,13 @@ func TestObjectUpload(t *testing.T) {
//test upload with empty reader (should create zero-byte-sized object)
obj = c.Object("upload5")
- err = obj.Upload(eofReader{}, nil, nil)
+ err = obj.Upload(eofReader{}, nil)
expectSuccess(t, err)
expectObjectContent(t, obj, nil)
//test upload without reader (should create zero-byte-sized object)
obj = c.Object("upload6")
- err = obj.Upload(nil, nil, nil)
+ err = obj.Upload(nil, nil)
expectSuccess(t, err)
expectObjectContent(t, obj, nil)
})
@@ -122,21 +122,21 @@ func TestObjectDownload(t *testing.T) {
testWithContainer(t, func(c *schwift.Container) {
//upload example object
obj := c.Object("example")
- err := obj.Upload(bytes.NewReader(objectExampleContent), nil, nil)
+ err := obj.Upload(bytes.NewReader(objectExampleContent), nil)
expectSuccess(t, err)
//test download as string
- str, err := obj.Download(nil, nil).AsString()
+ str, err := obj.Download(nil).AsString()
expectSuccess(t, err)
expectString(t, str, string(objectExampleContent))
//test download as byte slice
- buf, err := obj.Download(nil, nil).AsByteSlice()
+ buf, err := obj.Download(nil).AsByteSlice()
expectSuccess(t, err)
expectString(t, string(buf), string(objectExampleContent))
//test download as io.ReadCloser slice
- reader, err := obj.Download(nil, nil).AsReadCloser()
+ reader, err := obj.Download(nil).AsReadCloser()
expectSuccess(t, err)
buf = make([]byte, 4)
_, err = reader.Read(buf)
@@ -156,14 +156,14 @@ func TestObjectUpdate(t *testing.T) {
obj := c.Object("example")
//test that metadata update fails for non-existing object
- newHeaders := make(schwift.ObjectHeaders)
+ newHeaders := schwift.NewObjectHeaders()
newHeaders.ContentType().Set("application/json")
err := obj.Update(newHeaders, nil)
expectBool(t, schwift.Is(err, http.StatusNotFound), true)
expectError(t, err, "expected 202 response, got 404 instead: <html><h1>Not Found</h1><p>The resource could not be found.</p></html>")
//create object
- err = obj.Upload(nil, nil, nil)
+ err = obj.Upload(nil, nil)
expectSuccess(t, err)
hdr, err := obj.Headers()
@@ -183,12 +183,12 @@ func TestObjectUpdate(t *testing.T) {
func TestObjectCopyMove(t *testing.T) {
testWithContainer(t, func(c *schwift.Container) {
obj1 := c.Object("location1")
- err := obj1.Upload(bytes.NewReader(objectExampleContent), nil, nil)
+ err := obj1.Upload(bytes.NewReader(objectExampleContent), nil)
expectSuccess(t, err)
expectObjectExistence(t, obj1, true)
obj2 := c.Object("location2")
- expectSuccess(t, obj1.CopyTo(obj2, nil, nil))
+ expectSuccess(t, obj1.CopyTo(obj2, nil))
expectObjectExistence(t, obj1, true)
expectObjectExistence(t, obj2, true)
expectObjectContent(t, obj2, objectExampleContent)
@@ -212,7 +212,7 @@ func expectObjectExistence(t *testing.T, obj *schwift.Object, expectedExists boo
func expectObjectContent(t *testing.T, obj *schwift.Object, expected []byte) {
t.Helper()
- str, err := obj.Download(nil, nil).AsString()
+ str, err := obj.Download(nil).AsString()
expectSuccess(t, err)
expectString(t, str, string(expected))
obj.Invalidate()
diff --git a/tests/shared_test.go b/tests/shared_test.go
index 187d0b4..c35951f 100644
--- a/tests/shared_test.go
+++ b/tests/shared_test.go
@@ -99,9 +99,9 @@ func testWithContainer(t *testing.T, testCode func(c *schwift.Container)) {
expectSuccess(t, err)
if exists {
expectSuccess(t, container.Objects().Foreach(func(o *schwift.Object) error {
- return o.Delete(nil, nil)
+ return o.Delete(nil)
}))
- err = container.Delete(nil, nil)
+ err = container.Delete(nil)
expectSuccess(t, err)
}
})