Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preview versioning #175

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ install:
- if [[ $TRAVIS_GO_VERSION = '1.7' || $TRAVIS_GO_VERSION > '1.7' ]]; then go get golang.org/x/time/rate
; fi
script:
- if [ -n "$OSS_TEST_ACCESS_KEY_ID" ]; then
cd oss;
travis_wait 30 go test -v -covermode=count -coverprofile=coverage.out -timeout=30m;
$HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci;
fi
- if [[ ! -n "$OSS_TEST_ACCESS_KEY_ID" ]]; then exit 0
; fi

- cd oss
- travis_wait 30 go test -v -covermode=count -coverprofile=coverage.out -timeout=30m
- "$HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci"

env:
global:
- secure: ZCL5egxJmZA+o1ujsaJe//AIh1ag/exSUJ2FzoKPF3O9c84hMc2k5EYO2rGzTNn1ML6M89Mo5hAvHQhyJEHjRuMtjc1QrfxAaA3mqm4scGXIXesPMqYGuvuPSh++6/fkAwaVBAhrk5GaDG1/FuxHE5zusGx3SvGegnCwO7n/2YCfXco6DWgVCdrz4p1EpPkAM3JIdHFUzsDWiimVuiNAvJmAT8+IeOPTT+WgusCJj4ORS3X3LddTjttBP+hRrp/pGSoNqPMzfysWybtaL2SJ8URtvsxW0Mo5BwocHAxAhPP+M2OscQbDzthSAezCLngYvrfBplfIyWlahlgzNz/FjXz5pQwWdYVNoibyxLLMOH685n75LNONN/xVO/GFmVPx7DMGapkN5NzIWS62D4v8QrRkwtms42OUkyEUHjDh8Evui3K2MNJVXA3TI9zOAR+C0krD7OEyS37qrppodhRxJSqFUlgXnk//wLldMC7vleDd7L2UQSWjqyBHqFOgsVaiLU2KRTY3zvv7ke+dqb5VF31mH6qAr8lJTR9un8M1att0VwCEKxoIRT4cKJCpEtZd8ovXOVt1uE695ThVXE9I5e00GXdTzqXOuv6zT4hv/dgmbz9JN9MYeCwmokEoIUmJKNYERa/bNVVefdnJt7h+dm+KpyPAS+XvPLzjbnWdYNA=
Expand Down
207 changes: 173 additions & 34 deletions oss/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,17 @@ func (bucket Bucket) DoGetObject(request *GetObjectRequest, options []Option) (*
//
func (bucket Bucket) CopyObject(srcObjectKey, destObjectKey string, options ...Option) (CopyObjectResult, error) {
var out CopyObjectResult
options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))

//first find version id
versionIdKey := "versionId"
versionId, _ := findOption(options, versionIdKey, nil)
if versionId == nil {
options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
} else {
options = deleteOption(options, versionIdKey)
options = append(options, CopySourceVersion(bucket.BucketName, url.QueryEscape(srcObjectKey), versionId.(string)))
}

params := map[string]interface{}{}
resp, err := bucket.do("PUT", destObjectKey, params, options, nil, nil)
if err != nil {
Expand Down Expand Up @@ -281,14 +291,32 @@ func (bucket Bucket) CopyObjectFrom(srcBucketName, srcObjectKey, destObjectKey s

func (bucket Bucket) copy(srcObjectKey, destBucketName, destObjectKey string, options ...Option) (CopyObjectResult, error) {
var out CopyObjectResult
options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))

//first find version id
versionIdKey := "versionId"
versionId, _ := findOption(options, versionIdKey, nil)
if versionId == nil {
options = append(options, CopySource(bucket.BucketName, url.QueryEscape(srcObjectKey)))
} else {
options = deleteOption(options, versionIdKey)
options = append(options, CopySourceVersion(bucket.BucketName, url.QueryEscape(srcObjectKey), versionId.(string)))
}

headers := make(map[string]string)
err := handleOptions(headers, options)
if err != nil {
return out, err
}
params := map[string]interface{}{}
resp, err := bucket.Client.Conn.Do("PUT", destBucketName, destObjectKey, params, headers, nil, 0, nil)

// get response header
respHeader, _ := findOption(options, responseHeader, nil)
if respHeader != nil {
pRespHeader := respHeader.(*http.Header)
*pRespHeader = resp.Headers
}

if err != nil {
return out, err
}
Expand Down Expand Up @@ -357,6 +385,14 @@ func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Opti
handleOptions(headers, opts)
resp, err := bucket.Client.Conn.Do("POST", bucket.BucketName, request.ObjectKey, params, headers,
request.Reader, initCRC, listener)

// get response header
respHeader, _ := findOption(options, responseHeader, nil)
if respHeader != nil {
pRespHeader := respHeader.(*http.Header)
*pRespHeader = resp.Headers
}

if err != nil {
return nil, err
}
Expand Down Expand Up @@ -384,9 +420,9 @@ func (bucket Bucket) DoAppendObject(request *AppendObjectRequest, options []Opti
//
// error it's nil if no error, otherwise it's an error object.
//
func (bucket Bucket) DeleteObject(objectKey string) error {
params := map[string]interface{}{}
resp, err := bucket.do("DELETE", objectKey, params, nil, nil, nil)
func (bucket Bucket) DeleteObject(objectKey string, options ...Option) error {
params, _ := getRawParams(options)
resp, err := bucket.do("DELETE", objectKey, params, options, nil, nil)
if err != nil {
return err
}
Expand All @@ -409,6 +445,63 @@ func (bucket Bucket) DeleteObjects(objectKeys []string, options ...Option) (Dele
for _, key := range objectKeys {
dxml.Objects = append(dxml.Objects, DeleteObject{Key: key})
}

isQuiet, _ := findOption(options, deleteObjectsQuiet, false)
dxml.Quiet = isQuiet.(bool)

bs, err := xml.Marshal(dxml)
if err != nil {
return out, err
}
buffer := new(bytes.Buffer)
buffer.Write(bs)

contentType := http.DetectContentType(buffer.Bytes())
options = append(options, ContentType(contentType))
sum := md5.Sum(bs)
b64 := base64.StdEncoding.EncodeToString(sum[:])
options = append(options, ContentMD5(b64))

params := map[string]interface{}{}
params["delete"] = nil
params["encoding-type"] = "url"

resp, err := bucket.do("POST", "", params, options, buffer, nil)
if err != nil {
return out, err
}
defer resp.Body.Close()

deletedResult := DeleteObjectVersionsResult{}
if !dxml.Quiet {
if err = xmlUnmarshal(resp.Body, &deletedResult); err == nil {
err = decodeDeleteObjectsResult(&deletedResult)
}
}

// Keep compatibility:need convert to struct DeleteObjectsResult
out.XMLName = deletedResult.XMLName
for _, v := range deletedResult.DeletedObjectsDetail {
out.DeletedObjects = append(out.DeletedObjects, v.Key)
}

return out, err
}

// DeleteObjectVersions deletes multiple object versions.
//
// objectVersions the object keys and versions to delete.
// options the options for deleting objects.
// Supported option is DeleteObjectsQuiet which means it will not return error even deletion failed (not recommended). By default it's not used.
//
// DeleteObjectVersionsResult the result object.
// error it's nil if no error, otherwise it's an error object.
//
func (bucket Bucket) DeleteObjectVersions(objectVersions []DeleteObject, options ...Option) (DeleteObjectVersionsResult, error) {
out := DeleteObjectVersionsResult{}
dxml := deleteXML{}
dxml.Objects = objectVersions

isQuiet, _ := findOption(options, deleteObjectsQuiet, false)
dxml.Quiet = isQuiet.(bool)

Expand Down Expand Up @@ -509,6 +602,32 @@ func (bucket Bucket) ListObjects(options ...Option) (ListObjectsResult, error) {
return out, err
}

// ListObjectVersions lists objects of all versions under the current bucket.
func (bucket Bucket) ListObjectVersions(options ...Option) (ListObjectVersionsResult, error) {
var out ListObjectVersionsResult

options = append(options, EncodingType("url"))
params, err := getRawParams(options)
if err != nil {
return out, err
}
params["versions"] = nil

resp, err := bucket.do("GET", "", params, options, nil, nil)
if err != nil {
return out, err
}
defer resp.Body.Close()

err = xmlUnmarshal(resp.Body, &out)
if err != nil {
return out, err
}

err = decodeListObjectVersionsResult(&out)
return out, err
}

// SetObjectMeta sets the metadata of the Object.
//
// objectKey object
Expand All @@ -533,7 +652,7 @@ func (bucket Bucket) SetObjectMeta(objectKey string, options ...Option) error {
// error it's nil if no error, otherwise it's an error object.
//
func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option) (http.Header, error) {
params := map[string]interface{}{}
params, _ := getRawParams(options)
resp, err := bucket.do("HEAD", objectKey, params, options, nil, nil)
if err != nil {
return nil, err
Expand All @@ -554,7 +673,7 @@ func (bucket Bucket) GetObjectDetailedMeta(objectKey string, options ...Option)
// error it's nil if no error, otherwise it's an error object.
//
func (bucket Bucket) GetObjectMeta(objectKey string, options ...Option) (http.Header, error) {
params := map[string]interface{}{}
params, _ := getRawParams(options)
params["objectMeta"] = nil
//resp, err := bucket.do("GET", objectKey, "?objectMeta", "", nil, nil, nil)
resp, err := bucket.do("HEAD", objectKey, params, options, nil, nil)
Expand Down Expand Up @@ -582,9 +701,9 @@ func (bucket Bucket) GetObjectMeta(objectKey string, options ...Option) (http.He
//
// error it's nil if no error, otherwise it's an error object.
//
func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType) error {
options := []Option{ObjectACL(objectACL)}
params := map[string]interface{}{}
func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType, options ...Option) error {
options = append(options, ObjectACL(objectACL))
params, _ := getRawParams(options)
params["acl"] = nil
resp, err := bucket.do("PUT", objectKey, params, options, nil, nil)
if err != nil {
Expand All @@ -601,11 +720,11 @@ func (bucket Bucket) SetObjectACL(objectKey string, objectACL ACLType) error {
// GetObjectACLResult the result object when error is nil. GetObjectACLResult.Acl is the object ACL.
// error it's nil if no error, otherwise it's an error object.
//
func (bucket Bucket) GetObjectACL(objectKey string) (GetObjectACLResult, error) {
func (bucket Bucket) GetObjectACL(objectKey string, options ...Option) (GetObjectACLResult, error) {
var out GetObjectACLResult
params := map[string]interface{}{}
params, _ := getRawParams(options)
params["acl"] = nil
resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
if err != nil {
return out, err
}
Expand All @@ -630,7 +749,7 @@ func (bucket Bucket) GetObjectACL(objectKey string) (GetObjectACLResult, error)
//
func (bucket Bucket) PutSymlink(symObjectKey string, targetObjectKey string, options ...Option) error {
options = append(options, symlinkTarget(url.QueryEscape(targetObjectKey)))
params := map[string]interface{}{}
params, _ := getRawParams(options)
params["symlink"] = nil
resp, err := bucket.do("PUT", symObjectKey, params, options, nil, nil)
if err != nil {
Expand All @@ -648,10 +767,10 @@ func (bucket Bucket) PutSymlink(symObjectKey string, targetObjectKey string, opt
// error it's nil if no error, otherwise it's an error object.
// When error is nil, the target file key is in the X-Oss-Symlink-Target header of the returned object.
//
func (bucket Bucket) GetSymlink(objectKey string) (http.Header, error) {
params := map[string]interface{}{}
func (bucket Bucket) GetSymlink(objectKey string, options ...Option) (http.Header, error) {
params, _ := getRawParams(options)
params["symlink"] = nil
resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
if err != nil {
return nil, err
}
Expand All @@ -678,10 +797,10 @@ func (bucket Bucket) GetSymlink(objectKey string) (http.Header, error) {
//
// error it's nil if no error, otherwise it's an error object.
//
func (bucket Bucket) RestoreObject(objectKey string) error {
params := map[string]interface{}{}
func (bucket Bucket) RestoreObject(objectKey string, options ...Option) error {
params, _ := getRawParams(options)
params["restore"] = nil
resp, err := bucket.do("POST", objectKey, params, nil, nil, nil)
resp, err := bucket.do("POST", objectKey, params, options, nil, nil)
if err != nil {
return err
}
Expand Down Expand Up @@ -911,9 +1030,9 @@ func (bucket Bucket) DoGetObjectWithURL(signedURL string, options []Option) (*Ge
//
// error it's nil if no error, otherwise it's an error object.
//
func (bucket Bucket) ProcessObject(objectKey string, process string) (ProcessObjectResult, error) {
func (bucket Bucket) ProcessObject(objectKey string, process string, options ...Option) (ProcessObjectResult, error) {
var out ProcessObjectResult
params := map[string]interface{}{}
params, _ := getRawParams(options)
params["x-oss-process"] = nil
processData := fmt.Sprintf("%v=%v", "x-oss-process", process)
data := strings.NewReader(processData)
Expand All @@ -935,7 +1054,7 @@ func (bucket Bucket) ProcessObject(objectKey string, process string) (ProcessObj
//
// error nil if success, otherwise error
//
func (bucket Bucket) PutObjectTagging(objectKey string, tagging ObjectTagging) error {
func (bucket Bucket) PutObjectTagging(objectKey string, tagging Tagging, options ...Option) error {
bs, err := xml.Marshal(tagging)
if err != nil {
return err
Expand All @@ -944,9 +1063,9 @@ func (bucket Bucket) PutObjectTagging(objectKey string, tagging ObjectTagging) e
buffer := new(bytes.Buffer)
buffer.Write(bs)

params := map[string]interface{}{}
params, _ := getRawParams(options)
params["tagging"] = nil
resp, err := bucket.do("PUT", objectKey, params, nil, buffer, nil)
resp, err := bucket.do("PUT", objectKey, params, options, buffer, nil)
if err != nil {
return err
}
Expand All @@ -963,12 +1082,12 @@ func (bucket Bucket) PutObjectTagging(objectKey string, tagging ObjectTagging) e
// Tagging
// error nil if success, otherwise error
//
func (bucket Bucket) GetObjectTagging(objectKey string) (ObjectTagging, error) {
var out ObjectTagging
params := map[string]interface{}{}
func (bucket Bucket) GetObjectTagging(objectKey string, options ...Option) (GetObjectTaggingResult, error) {
var out GetObjectTaggingResult
params, _ := getRawParams(options)
params["tagging"] = nil

resp, err := bucket.do("GET", objectKey, params, nil, nil, nil)
resp, err := bucket.do("GET", objectKey, params, options, nil, nil)
if err != nil {
return out, err
}
Expand All @@ -985,15 +1104,15 @@ func (bucket Bucket) GetObjectTagging(objectKey string) (ObjectTagging, error) {
//
// error nil if success, otherwise error
//
func (bucket Bucket) DeleteObjectTagging(objectKey string) error {
params := map[string]interface{}{}
func (bucket Bucket) DeleteObjectTagging(objectKey string, options ...Option) error {
params, _ := getRawParams(options)
params["tagging"] = nil

if objectKey == "" {
return fmt.Errorf("invalid argument: object name is empty")
}

resp, err := bucket.do("DELETE", objectKey, params, nil, nil, nil)
resp, err := bucket.do("DELETE", objectKey, params, options, nil, nil)
if err != nil {
return err
}
Expand All @@ -1010,8 +1129,18 @@ func (bucket Bucket) do(method, objectName string, params map[string]interface{}
if err != nil {
return nil, err
}
return bucket.Client.Conn.Do(method, bucket.BucketName, objectName,

resp, err := bucket.Client.Conn.Do(method, bucket.BucketName, objectName,
params, headers, data, 0, listener)

// get response header
respHeader, _ := findOption(options, responseHeader, nil)
if respHeader != nil {
pRespHeader := respHeader.(*http.Header)
*pRespHeader = resp.Headers
}

return resp, err
}

func (bucket Bucket) doURL(method HTTPMethod, signedURL string, params map[string]interface{}, options []Option,
Expand All @@ -1021,7 +1150,17 @@ func (bucket Bucket) doURL(method HTTPMethod, signedURL string, params map[strin
if err != nil {
return nil, err
}
return bucket.Client.Conn.DoURL(method, signedURL, headers, data, 0, listener)

resp, err := bucket.Client.Conn.DoURL(method, signedURL, headers, data, 0, listener)

// get response header
respHeader, _ := findOption(options, responseHeader, nil)
if respHeader != nil {
pRespHeader := respHeader.(*http.Header)
*pRespHeader = resp.Headers
}

return resp, err
}

func (bucket Bucket) getConfig() *Config {
Expand Down
Loading