1
0
mirror of https://github.com/rclone/rclone.git synced 2025-12-20 10:13:20 +00:00

vendor: switch to using go1.11 modules

This commit is contained in:
Nick Craig-Wood
2018-08-28 15:27:07 +01:00
parent 5c75453aba
commit da1682a30e
6142 changed files with 390 additions and 5155875 deletions

View File

@@ -1,259 +0,0 @@
package image
import (
"fmt"
"reflect"
"strings"
"github.com/yunify/qingstor-sdk-go/service"
)
const (
// ActionSep is separator of action.
ActionSep = ":"
// OPSep is separator of operation.
OPSep = "|"
// KVSep is separator of Key-Value.
KVSep = "_"
//KVPairSep is separator of args.
KVPairSep = ","
)
const (
// InfoOperation is string of info operation.
InfoOperation string = "info"
// CropOperation is string of crop operation.
CropOperation string = "crop"
// FormatOperation is string of format operation.
FormatOperation string = "format"
// ResizeOperation is string of resize operation.
ResizeOperation string = "resize"
// RotateOperation is string of rotate operation.
RotateOperation string = "rotate"
// WaterMarkOperation is string of watermark operation.
WaterMarkOperation string = "watermark"
// WaterMarkImageOperation is string of watermark image operation.
WaterMarkImageOperation string = "watermark_image"
)
// ResizeMode is the type of resize mode.
type ResizeMode int
const (
// ResizeFixed resizes image to fix width and height.
ResizeFixed ResizeMode = iota
// ResizeForce resizes image to force witdth and height.
ResizeForce
// ResizeThumbnail resizes image to thumbnail width and height.
ResizeThumbnail
)
// CropGravity is the type of crop gravity.
type CropGravity int
const (
// CropCenter crops image to center width and height.
CropCenter CropGravity = iota
// CropNorth crops image to north width and height.
CropNorth
// CropEast crops image to east width and height.
CropEast
// CropSouth crops image to south width and height.
CropSouth
// CropWest crops image to west width and height.
CropWest
// CropNorthWest crops image to north west width and height.
CropNorthWest
// CropNorthEast crops image to north east width and height.
CropNorthEast
// CropSouthWest crops image to south west width and height.
CropSouthWest
// CropSouthEast crops image to south east width and height.
CropSouthEast
// CropAuto crops image to auto width and height.
CropAuto
)
// Image is struct of Image process.
type Image struct {
key *string
bucket *service.Bucket
input *service.ImageProcessInput
}
// Init initializes an image to process.
func Init(bucket *service.Bucket, objectKey string) *Image {
return &Image{
key: &objectKey,
bucket: bucket,
input: &service.ImageProcessInput{},
}
}
// Info gets the information of the image.
func (image *Image) Info() *Image {
return image.setActionParam(InfoOperation, nil)
}
// RotateParam is param of the rotate operation.
type RotateParam struct {
Angle int `schema:"a"`
}
// Rotate image.
func (image *Image) Rotate(param *RotateParam) *Image {
return image.setActionParam(RotateOperation, param)
}
// ResizeParam is param of the resize operation.
type ResizeParam struct {
Width int `schema:"w,omitempty"`
Height int `schema:"h,omitempty"`
Mode ResizeMode `schema:"m"`
}
// Resize image.
func (image *Image) Resize(param *ResizeParam) *Image {
return image.setActionParam(ResizeOperation, param)
}
// CropParam is param of the crop operation.
type CropParam struct {
Width int `schema:"w,omitempty"`
Height int `schema:"h,omitempty"`
Gravity CropGravity `schema:"g"`
}
// Crop image.
func (image *Image) Crop(param *CropParam) *Image {
return image.setActionParam(CropOperation, param)
}
// FormatParam is param of the format operation.
type FormatParam struct {
Type string `schema:"t"`
}
// Format image.
func (image *Image) Format(param *FormatParam) *Image {
return image.setActionParam(FormatOperation, param)
}
// WaterMarkParam is param of the wartermark operation.
type WaterMarkParam struct {
Dpi int `schema:"d,omitempty"`
Opacity float64 `schema:"p,omitempty"`
Text string `schema:"t"`
Color string `schema:"c"`
}
// WaterMark is operation of watermark text content.
func (image *Image) WaterMark(param *WaterMarkParam) *Image {
return image.setActionParam(WaterMarkOperation, param)
}
// WaterMarkImageParam is param of the waterMark image operation
type WaterMarkImageParam struct {
Left int `schema:"l"`
Top int `schema:"t"`
Opacity float64 `schema:"p,omitempty"`
URL string `schema:"u"`
}
// WaterMarkImage is operation of watermark image.
func (image *Image) WaterMarkImage(param *WaterMarkImageParam) *Image {
return image.setActionParam(WaterMarkImageOperation, param)
}
// Process does Image process.
func (image *Image) Process() (*service.ImageProcessOutput, error) {
defer func(input *service.ImageProcessInput) {
input.Action = nil
}(image.input)
return image.bucket.ImageProcess(*image.key, image.input)
}
func (image *Image) setActionParam(operation string, param interface{}) *Image {
uri := operation
if param != nil {
uri = fmt.Sprintf("%s%s%s", uri, ActionSep, buildOptParamStr(param))
}
if image.input.Action != nil {
uri = fmt.Sprintf("%s%s%s", *image.input.Action, OPSep, uri)
}
image.input.Action = &uri
return image
}
func buildOptParamStr(param interface{}) string {
v := reflect.ValueOf(param).Elem()
var kvPairs []string
for i := 0; i < v.NumField(); i++ {
vf := v.Field(i)
tf := v.Type().Field(i)
key := tf.Tag.Get("schema")
value := vf.Interface()
tagValues := strings.Split(key, ",")
if isEmptyValue(vf) &&
len(tagValues) == 2 &&
tagValues[1] == "omitempty" {
continue
}
key = tagValues[0]
kvPairs = append(kvPairs, fmt.Sprintf("%v%s%v", key, KVSep, value))
}
return strings.Join(kvPairs, KVPairSep)
}
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array,
reflect.Map,
reflect.Slice,
reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64:
return v.Int() == 0
case reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64,
reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32,
reflect.Float64:
return v.Float() == 0
case reflect.Ptr:
return v.IsNil()
}
return false
}

View File

@@ -1,63 +0,0 @@
package image
import (
"testing"
"github.com/stretchr/testify/assert"
qs "github.com/yunify/qingstor-sdk-go/service"
)
var image *Image
func init() {
bucket := &qs.Bucket{}
// test.jpg is only a string
image = Init(bucket, "test.jpg")
}
func TestQueryString(t *testing.T) {
var param interface{}
param = &RotateParam{
Angle: 90,
}
image.setActionParam(RotateOperation, param)
assert.Equal(t, *image.input.Action, "rotate:a_90")
param = &CropParam{
Width: 300,
Height: 400,
Gravity: 0,
}
image.setActionParam(CropOperation, param)
assert.Equal(t, *image.input.Action, "rotate:a_90|crop:w_300,h_400,g_0")
param = &ResizeParam{
Width: 500,
Height: 500,
Mode: ResizeForce,
}
image.setActionParam(ResizeOperation, param)
assert.Equal(t, *image.input.Action, "rotate:a_90|crop:w_300,h_400,g_0|resize:w_500,h_500,m_1")
param = &FormatParam{
Type: "png",
}
image.setActionParam(FormatOperation, param)
assert.Equal(t, *image.input.Action, "rotate:a_90|crop:w_300,h_400,g_0|resize:w_500,h_500,m_1|format:t_png")
param = &WaterMarkParam{
Text: "5rC05Y2w5paH5a2X",
}
image.setActionParam(WaterMarkOperation, param)
assert.Equal(t, *image.input.Action, "rotate:a_90|crop:w_300,h_400,g_0|resize:w_500,h_500,m_1|format:t_png|watermark:t_5rC05Y2w5paH5a2X,c_")
param = &WaterMarkImageParam{
URL: "aHR0cHM6Ly9wZWszYS5xaW5nc3Rvci5jb20vaW1nLWRvYy1lZy9xaW5jbG91ZC5wbmc",
}
image.setActionParam(WaterMarkImageOperation, param)
assert.Equal(t, *image.input.Action, "rotate:a_90|crop:w_300,h_400,g_0|resize:w_500,h_500,m_1|format:t_png|watermark:t_5rC05Y2w5paH5a2X,c_|watermark_image:l_0,t_0,u_aHR0cHM6Ly9wZWszYS5xaW5nc3Rvci5jb20vaW1nLWRvYy1lZy9xaW5jbG91ZC5wbmc")
image.setActionParam(InfoOperation, nil)
assert.Equal(t, *image.input.Action, "rotate:a_90|crop:w_300,h_400,g_0|resize:w_500,h_500,m_1|format:t_png|watermark:t_5rC05Y2w5paH5a2X,c_|watermark_image:l_0,t_0,u_aHR0cHM6Ly9wZWszYS5xaW5nc3Rvci5jb20vaW1nLWRvYy1lZy9xaW5jbG91ZC5wbmc|info")
}

View File

@@ -1,106 +0,0 @@
package upload
import (
"bytes"
"errors"
"io"
)
const (
// QingStor has a max upload parts limit to 10000.
maxUploadParts = 10000
// We read from stream for read 1024B.
segmentSize = 1024
)
// chunk provides a struct to read file
type chunk struct {
fd io.Reader
cur int64
size int64
partSize int
}
// newChunk creates a FileChunk struct
func newChunk(fd io.Reader, partSize int) *chunk {
f := &chunk{
fd: fd,
partSize: partSize,
}
f.initSize()
return f
}
// nextPart reads the next part of the file
func (f *chunk) nextPart() (io.ReadSeeker, error) {
type readerAtSeeker interface {
io.ReaderAt
io.ReadSeeker
}
switch r := f.fd.(type) {
case readerAtSeeker:
var sectionSize int64
var err error
leftSize := f.size - f.cur
if leftSize >= int64(f.partSize) {
sectionSize = int64(f.partSize)
} else if leftSize > 0 {
sectionSize = f.size - f.cur
} else {
err = io.EOF
}
seekReader := io.NewSectionReader(r, f.cur, sectionSize)
f.cur += sectionSize
return seekReader, err
case io.Reader:
buf := make([]byte, segmentSize)
var n, lenBuf int
var err error
var chunk []byte
for {
n, _ = r.Read(buf)
if n == 0 {
if lenBuf == 0 {
err = io.EOF
}
break
}
lenBuf = lenBuf + n
chunk = append(chunk, buf...)
if lenBuf == f.partSize {
break
}
}
partBody := bytes.NewReader(chunk[:lenBuf])
return partBody, err
default:
return nil, errors.New("file does not support read")
}
}
// initSize tries to detect the total stream size, setting u.size. If
// the size is not known, size is set to -1.
func (f *chunk) initSize() {
f.size = -1
switch r := f.fd.(type) {
case io.Seeker:
pos, _ := r.Seek(0, 1)
defer r.Seek(pos, 0)
n, err := r.Seek(0, 2)
if err != nil {
return
}
f.size = n
// Try to adjust partSize if it is too small and account for
// integer division truncation.
if f.size/int64(f.partSize) >= int64(maxUploadParts) {
// Add one to the part size to account for remainders
// during the size calculation. e.g odd number of bytes.
f.partSize = int(f.size/int64(maxUploadParts)) + 1
}
}
}

View File

@@ -1,52 +0,0 @@
package upload
import (
"fmt"
"os"
"os/exec"
"testing"
)
var partSize = 5 * 1024
//Test_newFileChunk is the test function for New
func Test_newFileChunk(t *testing.T) {
setup()
fd, _ := os.Open("test_file")
defer fd.Close()
fr := newChunk(fd, partSize)
if fr.size != 512000 {
t.Fatalf("expected 512000, got %d", fr.size)
}
tearDown()
}
// Test_nextPart is the test function for nextSeekablePart
func Test_nextPart(t *testing.T) {
setup()
fd, _ := os.Open("test_file")
defer fd.Close()
fr := newChunk(fd, partSize)
partBody, err := fr.nextPart()
if err != nil {
fmt.Println(err)
}
temp := make([]byte, 6000)
n, _ := partBody.Read(temp)
if n != partSize {
t.Fatalf("expected 5120, got %d", len(temp))
}
tearDown()
}
func setup() {
exec.Command("dd", "if=/dev/zero", "of=test_file", "bs=1024", "count=500").Output()
}
func tearDown() {
exec.Command("rm", "", "test_file").Output()
}

View File

@@ -1,110 +0,0 @@
package upload
import (
"errors"
"github.com/yunify/qingstor-sdk-go/logger"
"github.com/yunify/qingstor-sdk-go/service"
"io"
)
// Uploader struct provides a struct to upload
type Uploader struct {
bucket *service.Bucket
partSize int
}
const smallestPartSize int = 1024 * 1024 * 4
//Init creates a uploader struct
func Init(bucket *service.Bucket, partSize int) *Uploader {
return &Uploader{
bucket: bucket,
partSize: partSize,
}
}
// Upload uploads multi parts of large object
func (u *Uploader) Upload(fd io.Reader, objectKey string) error {
if u.partSize < smallestPartSize {
logger.Errorf(nil, "Part size error")
return errors.New("the part size is too small")
}
uploadID, err := u.init(objectKey)
if err != nil {
logger.Errorf(nil, "Init multipart upload error, %v.", err)
return err
}
partNumbers, err := u.upload(fd, uploadID, objectKey)
if err != nil {
logger.Errorf(nil, "Upload multipart error, %v.", err)
return err
}
err = u.complete(objectKey, uploadID, partNumbers)
if err != nil {
logger.Errorf(nil, "Complete upload error, %v.", err)
return err
}
return nil
}
func (u *Uploader) init(objectKey string) (*string, error) {
output, err := u.bucket.InitiateMultipartUpload(
objectKey,
&service.InitiateMultipartUploadInput{},
)
if err != nil {
return nil, err
}
return output.UploadID, nil
}
func (u *Uploader) upload(fd io.Reader, uploadID *string, objectKey string) ([]*service.ObjectPartType, error) {
var partCnt int
partNumbers := []*service.ObjectPartType{}
fileReader := newChunk(fd, u.partSize)
for {
partBody, err := fileReader.nextPart()
if err == io.EOF {
break
}
if err != nil {
logger.Errorf(nil, "Get next part failed, %v", err)
return nil, err
}
_, err = u.bucket.UploadMultipart(
objectKey,
&service.UploadMultipartInput{
UploadID: uploadID,
PartNumber: &partCnt,
Body: partBody,
},
)
if err != nil {
logger.Errorf(nil, "Upload multipart failed, %v", err)
return nil, err
}
partNumbers = append(partNumbers, &service.ObjectPartType{
PartNumber: service.Int(partCnt - 0),
})
partCnt++
}
return partNumbers, nil
}
func (u *Uploader) complete(objectKey string, uploadID *string, partNumbers []*service.ObjectPartType) error {
_, err := u.bucket.CompleteMultipartUpload(
objectKey,
&service.CompleteMultipartUploadInput{
UploadID: uploadID,
ObjectParts: partNumbers,
},
)
if err != nil {
return err
}
return nil
}

View File

@@ -1,108 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package config
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/yunify/qingstor-sdk-go/logger"
)
func TestConfig(t *testing.T) {
c := Config{
AccessKeyID: "AccessKeyID",
SecretAccessKey: "SecretAccessKey",
Host: "qingstor.dev",
Port: 443,
Protocol: "https",
ConnectionRetries: 10,
LogLevel: "warn",
}
assert.Equal(t, "AccessKeyID", c.AccessKeyID)
assert.Equal(t, "SecretAccessKey", c.SecretAccessKey)
assert.Equal(t, "qingstor.dev", c.Host)
assert.Equal(t, 10, c.ConnectionRetries)
assert.Equal(t, "warn", c.LogLevel)
c.AdditionalUserAgent = `"`
assert.Error(t, c.Check())
c.AdditionalUserAgent = `test/user`
assert.NoError(t, c.Check())
}
func TestLoadDefaultConfig(t *testing.T) {
config := Config{}
config.LoadDefaultConfig()
assert.Equal(t, "", config.AccessKeyID)
assert.Equal(t, "", config.SecretAccessKey)
assert.Equal(t, "https", config.Protocol)
assert.Equal(t, "qingstor.com", config.Host)
assert.Equal(t, "", config.AdditionalUserAgent)
assert.Equal(t, "WARN", logger.GetLevel())
}
func TestLoadUserConfig(t *testing.T) {
config := Config{}
config.LoadUserConfig()
assert.NotNil(t, config.Host)
assert.NotNil(t, config.Protocol)
}
func TestLoadConfigFromContent(t *testing.T) {
fileContent := `
access_key_id: 'access_key_id'
secret_access_key: 'secret_access_key'
log_level: 'debug'
`
config := Config{}
config.LoadConfigFromContent([]byte(fileContent))
assert.Equal(t, "access_key_id", config.AccessKeyID)
assert.Equal(t, "secret_access_key", config.SecretAccessKey)
assert.Equal(t, "https", config.Protocol)
assert.Equal(t, "qingstor.com", config.Host)
assert.Equal(t, "DEBUG", logger.GetLevel())
}
func TestNewDefault(t *testing.T) {
config, err := NewDefault()
assert.Nil(t, err)
assert.Equal(t, "", config.AccessKeyID)
assert.Equal(t, "", config.SecretAccessKey)
assert.Equal(t, "https", config.Protocol)
assert.Equal(t, "qingstor.com", config.Host)
assert.Equal(t, 3, config.ConnectionRetries)
}
func TestNew(t *testing.T) {
config, err := New("AccessKeyID", "SecretAccessKey")
assert.Nil(t, err)
assert.Equal(t, "AccessKeyID", config.AccessKeyID)
assert.Equal(t, "SecretAccessKey", config.SecretAccessKey)
assert.Equal(t, "https", config.Protocol)
assert.Equal(t, "qingstor.com", config.Host)
}

View File

@@ -1,82 +0,0 @@
# Configuration Guide
## Summary
This SDK uses a structure called "Config" to store and manage configuration, read comments of public functions in ["config/config.go"](https://github.com/yunify/qingstor-sdk-go/blob/master/config/config.go) for details.
Except for Access Key, you can also configure the API endpoint for private cloud usage scenario. All available configurable items are listed in the default configuration file.
___Default Configuration File:___
``` yaml
# QingStor services configuration
access_key_id: 'ACCESS_KEY_ID'
secret_access_key: 'SECRET_ACCESS_KEY'
host: 'qingstor.com'
port: 443
protocol: 'https'
connection_retries: 3
# Valid log levels are "debug", "info", "warn", "error", and "fatal".
log_level: 'warn'
```
## Usage
Just create a config structure instance with your API Access Key, and initialize services you need with Init() function of the target service.
### Code Snippet
Create default configuration
``` go
defaultConfig, _ := config.NewDefault()
```
Create configuration from Access Key
``` go
configuration, _ := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
anotherConfiguration := config.NewDefault()
anotherConfiguration.AccessKeyID = "ACCESS_KEY_ID"
anotherConfiguration.SecretAccessKey = "SECRET_ACCESS_KEY"
```
Load user configuration
``` go
userConfig, _ := config.NewDefault().LoadUserConfig()
```
Load configuration from config file
``` go
configFromFile, _ := config.NewDefault().LoadConfigFromFilepath("PATH/TO/FILE")
```
Change API endpoint
``` go
moreConfiguration, _ := config.NewDefault()
moreConfiguration.Protocol = "http"
moreConfiguration.Host = "api.private.com"
moreConfiguration.Port = 80
```
Change http timeout
``` go
customConfiguration, _ := config.NewDefault().LoadUserConfig()
// For the default value refers to DefaultHTTPClientSettings in config package
// ReadTimeout affect each call to HTTPResponse.Body.Read()
customConfiguration.HTTPSettings.ReadTimeout = 2 * time.Minute
// WriteTimeout affect each write in io.Copy while sending HTTPRequest
customConfiguration.HTTPSettings.WriteTimeout = 2 * time.Minute
// Re-initialize the client to take effect
customConfiguration.InitHTTPClient()
```

View File

@@ -1,273 +0,0 @@
# QingStor Image Processing Usage Guide
For processing the image stored in QingStor by a variety of basic operations, such as format, crop, watermark and so on.
Please see [QingStor Image API](https://docs.qingcloud.com/qingstor/data_process/image_process/index.html).
## Usage
Before using the image service, you need to initialize the [Configuration](https://github.com/yunify/qingstor-sdk-go/blob/master/docs/configuration.md) and [QingStor Service](https://github.com/yunify/qingstor-sdk-go/blob/master/docs/qingstor_service_usage.md).
``` go
//Import the latest version API
import (
"github.com/yunify/qingstor-sdk-go/config"
"github.com/yunify/qingstor-sdk-go/client/image"
qs "github.com/yunify/qingstor-sdk-go/service"
)
```
## Code Snippet
Create configuration from Access Key and Initialize the QingStor service with a configuration.
``` go
// Initialize the QingStor service with a configuration
config, _ := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
service, _ := qs.Init(config)
```
Initialize a QingStor bucket.
``` go
bucket, _ := service.Bucket("bucketName", "zoneID")
```
Initialize a image.
``` go
img := image.Init(bucket, "imageName")
```
Now you can use the the high level APIs or basic image process API to do the image operation.
Get the information of the image
``` go
imageProcessOutput, _ := img.Info().Process()
```
Crop the image.
``` go
imageProcessOutput, _ := img.Crop(&image.CropParam{
...operation_param...
}).Process()
```
Rotate the image.
``` go
imageProcessOutput, _ := img.Rotate(&image.RotateParam{
...operation_param...
}).Process()
```
Resize the image.
``` go
imageProcessOutput, _ := img.Resize(&image.ResizeParam{
...operation_param...
}).Process()
```
Watermark the image.
``` go
imageProcessOutput, _ := img.WaterMark(&image.WaterMarkParam{
...operation_param...
}).Process()
```
WaterMarkImage the image.
``` go
imageProcessOutput, _ : = img.WaterMarkImage(&image.WaterMarkImageParam{
...operation_param...
}).Process()
```
Format the image.
``` go
imageProcessOutput, _ := img.Format(&image.Format{
...operation_param...
}).Process()
```
Operation pipline, the image will be processed by order. The maximum number of operations in the pipeline is 10.
``` go
// Rotate and then resize the image
imageProcessOutput, _ := img.Rotate(&image.RotateParam{
... operation_param...
}).Resize(&image.ResizeParam{
... operation_param...
}).Process()
```
Use the original basic API to rotate the image 90 angles.
``` go
operation := "rotate:a_90"
imageProcessOutput, err := bucket.ImageProcess("imageName", &qs.ImageProcessInput{
Action: &operation})
```
`operation_param` is the image operation param, which definined in `qingstor-sdk-go/client/image/image.go`.
``` go
import "github.com/yunify/qingstor-sdk-go/service"
// client/image/image.go
type Image struct {
key *string
bucket *service.Bucket
input *service.ImageProcessInput
}
// About cropping image definition
type CropGravity int
const (
CropCenter CropGravity = iota
CropNorth
CropEast
CropSouth
CropWest
CropNorthWest
CropNorthEast
CropSouthWest
CropSouthEast
CropAuto
)
type CropParam struct {
Width int `schema:"w,omitempty"`
Height int `schema:"h,omitempty"`
Gravity CropGravity `schema:"g"`
}
// About rotating image definitions
type RotateParam struct {
Angle int `schema:"a"`
}
// About resizing image definitions
type ResizeMode int
type ResizeParam struct {
Width int `schema:"w,omitempty"`
Height int `schema:"h,omitempty"`
Mode ResizeMode `schema:"m"`
}
// On the definition of text watermarking
type WaterMarkParam struct {
Dpi int `schema:"d,omitempty"`
Opacity float64 `schema:"p,omitempty"`
Text string `schema:"t"`
Color string `schema:"c"`
}
// On the definition of image watermarking
type WaterMarkImageParam struct {
Left int `schema:"l"`
Top int `schema:"t"`
Opacity float64 `schema:"p,omitempty"`
URL string `schema:"u"`
}
// About image format conversion definitions
type FormatParam struct {
Type string `schema:"t"`
}
```
__Quick Start Code Example:__
Include a complete example, but the code needs to fill in your own information
``` go
package main
import (
"log"
"github.com/yunify/qingstor-sdk-go/client/image"
"github.com/yunify/qingstor-sdk-go/config"
qs "github.com/yunify/qingstor-sdk-go/service"
)
func main() {
// Load your configuration
// Replace here with your key pair
config, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
checkErr(err)
// Initialize QingStror Service
service, err := qs.Init(config)
checkErr(err)
// Initialize Bucket
// Replace here with your bucketName and zoneID
bucket, err := service.Bucket("bucketName", "zoneID")
checkErr(err)
// Initialize Image
// Replace here with your your ImageName
img := image.Init(bucket, "imageName")
checkErr(err)
// Because 0 is an invalid parameter, default not modify
imageProcessOutput, err := img.Crop(&image.CropParam{Width: 0}).Process()
checkErr(err)
testOutput(imageProcessOutput)
// Rotate the image 90 angles
imageProcessOutput, err = img.Rotate(&image.RotateParam{Angle: 90}).Process()
checkErr(err)
testOutput(imageProcessOutput)
// Text watermark, Watermark text content, encoded by base64.
imageProcessOutput, err = img.WaterMark(&image.WaterMarkParam{
Text: "5rC05Y2w5paH5a2X",
}).Process()
checkErr(err)
testOutput(imageProcessOutput)
// Image watermark, Watermark image url encoded by base64.
imageProcessOutput, err = img.WaterMarkImage(&image.WaterMarkImageParam{
URL: "aHR0cHM6Ly9wZWszYS5xaW5nc3Rvci5jb20vaW1nLWRvYy1lZy9xaW5jbG91ZC5wbmc",
}).Process()
checkErr(err)
testOutput(imageProcessOutput)
// Reszie the image with width 300px and height 400 px
imageProcessOutput, err = img.Resize(&image.ResizeParam{
Width: 300,
Height: 400,
}).Process()
checkErr(err)
testOutput(imageProcessOutput)
// Swap format to jpeg
imageProcessOutput, err = img.Format(&image.FormatParam{
Type: "jpeg",
}).Process()
checkErr(err)
testOutput(imageProcessOutput)
// Pipline model
// The maximum number of operations in the pipeline is 10
imageProcessOutput, err = img.Rotate(&image.RotateParam{
Angle: 270,
}).Resize(&image.ResizeParam{
Width: 300,
Height: 300,
}).Process()
checkErr(err)
testOutput(imageProcessOutput)
// Get the information of the image
imageProcessOutput, err = img.Info().Process()
checkErr(err)
testOutput(imageProcessOutput)
// Use the original api to rotate the image 90 angles
operation := "rotate:a_90"
imageProcessOutput, err = bucket.ImageProcess("imageName", &qs.ImageProcessInput{
Action: &operation})
checkErr(err)
defer imageProcessOutput.Close() // Don't forget to close the output otherwise will be leaking http connections
testOutput(imageProcessOutput)
}
// *qs.ImageProcessOutput: github.com/yunify/qingstor-sdk-go/service/object.go
func testOutput(out *qs.ImageProcessOutput) {
log.Println(*out.StatusCode)
log.Println(*out.RequestID)
log.Println(out.Body)
log.Println(*out.ContentLength)
}
func checkErr(err error) {
if err != nil {
log.Println(err)
}
}
```

View File

@@ -1,39 +0,0 @@
# Installation Guide
## Requirement
This SDK requires Go 1.6 and higher vendor feature, the dependencies this project uses are included in the `vendor` directory. We use [glide](https://glide.sh) to manage project dependences.
___Notice:___ _You can also use Go 1.5 with the `GO15VENDOREXPERIMENT=1`._
## Install from source code
Use `go get` to download this SDK from GitHub:
``` bash
$ go get -u github.com/yunify/qingstor-sdk-go
```
You can also download a specified version of zipped source code in the repository [releases page](https://github.com/yunify/qingstor-sdk-go/releases). The zipped source code only contains golang source code without unit test files.
___Examples:___
- *[qingstor-sdk-go-source-v0.7.1.zip](https://github.com/yunify/qingstor-sdk-go/releases/download/v0.7.1/qingstor-sdk-go-source-v0.7.1.zip)*
- *[qingstor-sdk-go-source-with-vendor-v0.7.1.zip](https://github.com/yunify/qingstor-sdk-go/releases/download/v0.7.1/qingstor-sdk-go-source-with-vendor-v0.7.1.zip)*
## Install from binary release (deprecated)
After Go 1.7, there's a new feature called Binary-Only Package. It allows distributing packages in binary form without including the source code used for compiling the package. For more information about Binary-Only Package, please read [_GoLang Package Build_](https://golang.org/pkg/go/build/) to know how to use that.
We provide Linux, macOS and Windows binary packages along with a header files. A header file only contains three lines of content, "//go:binary-only-package" is the first line, the second line is blank, and the second is the package name. There's one header file named "binary.go" for each golang package.
You can download a specified version of zipped binary release in the repository [releases page](https://github.com/yunify/qingstor-sdk-go/releases).
___Notice:___ _We didn't provide 386 version binary packages, since there's almost no one using a 386 machine._
___Examples:___
- *[qingstor-sdk-go-header-v0.7.1-go-1.7.zip](https://github.com/yunify/qingstor-sdk-go/releases/download/v0.7.1/qingstor-sdk-go-header-v0.7.1-go-1.7.zip)*
- *[qingstor-sdk-go-binary-v0.7.1-linux_amd64-go-1.7.zip](https://github.com/yunify/qingstor-sdk-go/releases/download/v0.7.1/qingstor-sdk-go-binary-v0.7.1-linux_amd64-go-1.7.zip)*
- *[qingstor-sdk-go-binary-v0.7.1-darwin_amd64-go-1.7.zip](https://github.com/yunify/qingstor-sdk-go/releases/download/v0.7.1/qingstor-sdk-go-binary-v0.7.1-darwin_amd64-go-1.7.zip)*
- *[qingstor-sdk-go-binary-v0.7.1-windows_amd64-go-1.7.zip](https://github.com/yunify/qingstor-sdk-go/releases/download/v0.7.1/qingstor-sdk-go-binary-v0.7.1-windows_amd64-go-1.7.zip)*

View File

@@ -1,256 +0,0 @@
# QingStor Service Usage Guide
Import the QingStor and initialize service with a config, and you are ready to use the initialized service. Service only contains one API, and it is "ListBuckets".
To use bucket related APIs, you need to initialize a bucket from service using "Bucket" function.
Each API function take a Input struct and return an Output struct. The Input struct consists of request params, request headers, request elements and request body, and the Output holds the HTTP status code, QingStor request ID, response headers, response elements, response body and error (if error occurred).
You can use a specified version of a service by import a service package with a date suffix.
``` go
import (
// Import the latest version API
"github.com/yunify/qingstor-sdk-go/config"
qs "github.com/yunify/qingstor-sdk-go/service"
qsErrors "github.com/yunify/qingstor-sdk-go/request/errors"
)
```
### Code Snippet
Initialize the QingStor service with a configuration
``` go
userConfig, err := config.NewDefault().LoadUserConfig()
if err != nil { panic(err) }
qsService, _ := qs.Init(userConfig)
```
List buckets
``` go
qsOutput, err := qsService.ListBuckets(nil)
// Print the HTTP status code.
// Example: 200
fmt.Println(qs.IntValue(qsOutput.StatusCode))
// Print the bucket count.
// Example: 5
fmt.Println(qs.IntValue(qsOutput.Count))
// Print the name of first bucket.
// Example: "test-bucket"
fmt.Println(qs.String(qsOutput.Buckets[0].Name))
```
Initialize a QingStor bucket
``` go
bucket, err := qsService.Bucket("test-bucket", "pek3a")
```
List objects in the bucket
``` go
bOutput, err := bucket.ListObjects(nil)
// Print the HTTP status code.
// Example: 200
fmt.Println(qs.IntValue(bOutput.StatusCode))
// Print the key count.
// Example: 7
fmt.Println(len(bOutput.Keys))
```
Set ACL of the bucket
``` go
bACLOutput, err := bucket.PutACL(&qs.PutBucketACLInput{
ACL: []*service.ACLType{{
Grantee: &service.GranteeType{
Type: qs.String("user"),
ID: qs.String("usr-xxxxxxxx"),
},
Permission: qs.String("FULL_CONTROL"),
}},
})
// Print the HTTP status code.
// Example: 200
fmt.Println(qs.IntValue(bACLOutput.StatusCode))
```
Put object
``` go
// Open file
file, err := os.Open("/tmp/Screenshot.jpg")
defer file.Close()
// Calculate MD5
hash := md5.New()
io.Copy(hash, file)
hashInBytes := hash.Sum(nil)[:16]
md5String := hex.EncodeToString(hashInBytes)
// Put object
putOutput, err := bucket.PutObject(
"Screenshot.jpg",
&service.PutObjectInput{
ContentLength: qs.Int(102475), // Obtain automatically if empty
ContentType: qs.String("image/jpeg"), // Detect automatically if empty
ContentMD5: qs.String(md5String),
Body: file,
},
)
if err != nil {
// Example: QingStor Error: StatusCode 403, Code "permission_denied"...
fmt.Println(err)
} else {
// Print the HTTP status code.
// Example: 201
fmt.Println(qs.IntValue(putOutput.StatusCode))
}
```
Get object
``` go
getOutput, err := bucket.GetObject(
"Screenshot.jpg",
&GetObjectInput{},
)
if err != nil {
// Example: QingStor Error: StatusCode 404, Code "object_not_exists"...
fmt.Println(err)
if qsErr, ok := err.(*qsErrors.QingStorError); ok {
println(qsErr.StatusCode, qsErr.Code)
}
} else {
defer getOutput.Close() // Don't forget to close, otherwise will be leaking connections
f, err := os.OpenFile("download_screenshot.jpg", os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
panic(err)
}
defer f.Close()
if _, err = io.Copy(f, getOutput.Body); err != nil {
panic(err)
}
}
```
Delete object
``` go
delOutput, err := bucket.DeleteObject("Screenshot.jpg")
// Print the HTTP status code.
// Example: 204
fmt.Println(qs.IntValue(delOutput.StatusCode))
```
Initialize Multipart Upload
``` go
initOutput, err := bucket.InitiateMultipartUpload(
"QingCloudInsight.mov",
&service.InitiateMultipartUploadInput{
ContentType: qs.String("video/quicktime"),
},
)
// Print the HTTP status code.
// Example: 200
fmt.Println(qs.IntValue(initOutput.StatusCode))
// Print the upload ID.
// Example: "9d37dd6ccee643075ca4e597ad65655c"
fmt.Println(qs.StringValue(initOutput.UploadID))
```
Upload Multipart
``` go
uploadOutput, err := bucket.UploadMultipart(
"QingCloudInsight.mov",
&service.UploadMultipartInput{
UploadID: qs.String("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
PartNumber: qs.Int(0),
ContentMD5: qs.String(md5String0),
Body: file0,
},
)
// Print the HTTP status code.
// Example: 201
fmt.Println(qs.IntValue(uploadOutput.StatusCode))
uploadOutput, err = bucket.UploadMultipart(
"QingCloudInsight.mov",
&service.UploadMultipartInput{
UploadID: qs.String("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
PartNumber: qs.Int(1),
ContentMD5: qs.String(md5String1),
Body: file1,
},
)
// Print the HTTP status code.
// Example: 201
fmt.Println(qs.IntValue(uploadOutput.StatusCode))
uploadOutput, err = bucket.UploadMultipart(
"QingCloudInsight.mov"
&service.UploadMultipartInput{
UploadID: qs.String("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
PartNumber: qs.Int(2),
ContentMD5: qs.String(md5String2),
Body: file2,
},
)
// Print the HTTP status code.
// Example: 201
fmt.Println(qs.IntValue(uploadOutput.StatusCode))
```
Complete Multipart Upload
``` go
completeOutput, err := bucket.CompleteMultipartUpload(
"QingCloudInsight.mov",
&service.CompleteMultipartUploadInput{
UploadID: qs.String("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
ObjectParts: []*service.ObjectPart{{
PartNumber: qs.Int(0),
}, {
PartNumber: qs.Int(1),
}, {
PartNumber: qs.Int(2),
}},
},
)
// Print the HTTP status code.
// Example: 200
fmt.Println(qs.IntValue(completeOutput.StatusCode))
```
Abort Multipart Upload
``` go
abrtOutput, err := bucket.AbortMultipartUpload(
"QingCloudInsight.mov"
&service.AbortMultipartUploadInput{
UploadID: qs.String("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
},
)
// Print the error message.
// Example: QingStor Error: StatusCode 400, Code...
fmt.Println(err)
```

View File

@@ -1,125 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package builder
import (
"bytes"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/yunify/qingstor-sdk-go/config"
"github.com/yunify/qingstor-sdk-go/request/data"
)
type FakeProperties struct {
A *string `name:"a"`
B *string `name:"b"`
CD *int `name:"c-d"`
}
type FakeInput struct {
ParamA *string `location:"query" name:"a"`
ParamB *string `location:"query" name:"b"`
ParamCD *int `location:"query" name:"c_d" default:"1024"`
HeaderA *string `location:"headers" name:"A"`
HeaderB *time.Time `location:"headers" name:"B" format:"RFC 822"`
HeaderCD *int `location:"headers" name:"C-D"`
ElementA *string `location:"elements" name:"a"`
ElementB *string `location:"elements" name:"b"`
ElementCD *int64 `location:"elements" name:"cd"`
Body *string `localtion:"body"`
}
func (i *FakeInput) Validate() error {
return nil
}
func String(v string) *string {
return &v
}
func Int(v int) *int {
return &v
}
func Int64(v int64) *int64 {
return &v
}
func Time(v time.Time) *time.Time {
return &v
}
func TestBaseBuilder_BuildHTTPRequest(t *testing.T) {
conf, err := config.NewDefault()
assert.Nil(t, err)
tz, err := time.LoadLocation("Asia/Shanghai")
assert.Nil(t, err)
builder := BaseBuilder{}
operation := &data.Operation{
Config: conf,
APIName: "This is API name",
ServiceName: "Base",
Properties: &FakeProperties{
A: String("property_a"),
B: String("property_b"),
CD: Int(0),
},
RequestMethod: "GET",
RequestURI: "/hello/<a>/<c-d>/<b>/world",
StatusCodes: []int{
200,
201,
},
}
inputValue := reflect.ValueOf(&FakeInput{
ParamA: String("param_a"),
ParamCD: Int(1024),
HeaderA: String("header_a"),
HeaderB: Time(time.Date(2016, 9, 1, 15, 30, 0, 0, tz)),
ElementA: String("element_a"),
ElementB: String("element_b"),
ElementCD: Int64(0),
Body: String("This is body string"),
})
httpRequest, err := builder.BuildHTTPRequest(operation, &inputValue)
assert.Nil(t, err)
assert.Equal(t, &map[string]string{
"a": "property_a",
"b": "property_b",
"c-d": "0",
}, builder.parsedProperties)
assert.Equal(t, &map[string]string{
"a": "param_a",
"c_d": "1024",
}, builder.parsedQuery)
assert.Equal(t, &map[string]string{
"A": "header_a",
"B": "Thu, 01 Sep 2016 07:30:00 GMT",
"Content-Type": "application/json",
}, builder.parsedHeaders)
assert.NotNil(t, httpRequest.Header.Get("Date"))
assert.Equal(t, "40", httpRequest.Header.Get("Content-Length"))
buffer := &bytes.Buffer{}
buffer.ReadFrom(httpRequest.Body)
httpRequest.Body.Close()
assert.Equal(t, "{\"a\":\"element_a\",\"b\":\"element_b\",\"cd\":0}", buffer.String())
}

View File

@@ -1,83 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package builder
import (
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/yunify/qingstor-sdk-go/config"
"github.com/yunify/qingstor-sdk-go/request/data"
)
type ObjectSubServiceProperties struct {
BucketName *string `json:"bucket-name" name:"bucket-name"`
ObjectKey *string `json:"object-key" name:"object-key"`
Zone *string `json:"zone" name:"zone"`
}
type GetObjectInput struct {
IfMatch *string `json:"If-Match" name:"If-Match" location:"headers"`
IfModifiedSince *time.Time `json:"If-Modified-Since" name:"If-Modified-Since" format:"RFC 822" location:"headers"`
IfNoneMatch *string `json:"If-None-Match" name:"If-None-Match" location:"headers"`
IfUnmodifiedSince time.Time `json:"If-Unmodified-Since" name:"If-Unmodified-Since" format:"RFC 822" location:"headers"`
// Specified range of the Object
Range *string `json:"Range" name:"Range" location:"headers"`
}
func (i *GetObjectInput) Validate() error {
return nil
}
func TestQingStorBuilder_BuildHTTPRequest(t *testing.T) {
conf, err := config.NewDefault()
assert.Nil(t, err)
conf.Host = "qingstor.dev"
tz, err := time.LoadLocation("Asia/Shanghai")
assert.Nil(t, err)
qsBuilder := &QingStorBuilder{}
operation := &data.Operation{
Config: conf,
APIName: "GET Object",
ServiceName: "QingStor",
Properties: &ObjectSubServiceProperties{
BucketName: String("test"),
ObjectKey: String("path/to/key.txt"),
Zone: String("beta"),
},
RequestMethod: "GET",
RequestURI: "/<bucket-name>/<object-key>",
StatusCodes: []int{
201,
},
}
inputValue := reflect.ValueOf(&GetObjectInput{
IfModifiedSince: Time(time.Date(2016, 9, 1, 15, 30, 0, 0, tz)),
Range: String("100-"),
})
httpRequest, err := qsBuilder.BuildHTTPRequest(operation, &inputValue)
assert.Nil(t, err)
assert.NotNil(t, httpRequest.Header.Get("Date"))
assert.Equal(t, "0", httpRequest.Header.Get("Content-Length"))
assert.Equal(t, "", httpRequest.Header.Get("If-Match"))
assert.Equal(t, "Thu, 01 Sep 2016 07:30:00 GMT", httpRequest.Header.Get("If-Modified-Since"))
assert.Equal(t, "100-", httpRequest.Header.Get("Range"))
assert.Equal(t, "https://beta.qingstor.dev:443/test/path/to/key.txt", httpRequest.URL.String())
}

View File

@@ -1,136 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package request
import (
"bytes"
"io/ioutil"
"net/http"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/yunify/qingstor-sdk-go/config"
"github.com/yunify/qingstor-sdk-go/logger"
"github.com/yunify/qingstor-sdk-go/request/data"
"github.com/yunify/qingstor-sdk-go/request/errors"
)
type SomeActionProperties struct {
A *string `json:"a" name:"a"`
B *string `json:"b" name:"b"`
CD *string `json:"c-d" name:"c-d"`
}
type SomeActionInput struct {
Date *time.Time `json:"Date" name:"Date" format:"RFC 822" location:"headers"`
IfModifiedSince *time.Time `json:"If-Modified-Since" name:"If-Modified-Since" format:"RFC 822" location:"headers"`
Range *string `json:"Range" name:"Range" location:"headers"`
UploadID *string `json:"upload_id" name:"upload_id" location:"query"`
Count *int `json:"count" name:"count" location:"elements"`
}
func (s *SomeActionInput) Validate() error {
return nil
}
type SomeActionOutput struct {
StatusCode *int `location:"statusCode"`
Error *errors.QingStorError
RequestID *string `location:"requestID"`
}
func String(v string) *string {
return &v
}
func Int(v int) *int {
return &v
}
func Time(v time.Time) *time.Time {
return &v
}
func TestRequestSend(t *testing.T) {
conf, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
assert.Nil(t, err)
logger.SetLevel("warn")
operation := &data.Operation{
Config: conf,
Properties: &SomeActionProperties{
A: String("aaa"),
B: String("bbb"),
CD: String("ccc-ddd"),
},
APIName: "Some Action",
RequestMethod: "GET",
RequestURI: "/<a>/<b>/<c-d>",
StatusCodes: []int{
200, // OK
206, // Partial content
304, // Not modified
412, // Precondition failed
},
}
output := &SomeActionOutput{}
r, err := New(operation, &SomeActionInput{
Date: Time(time.Date(2016, 9, 1, 15, 30, 0, 0, time.UTC)),
IfModifiedSince: Time(time.Date(2016, 9, 1, 15, 30, 0, 0, time.UTC)),
Range: String("100-"),
UploadID: String("0"),
Count: Int(23),
}, output)
assert.Nil(t, err)
err = r.build()
assert.Nil(t, err)
err = r.sign()
assert.Nil(t, err)
assert.Equal(t, r.HTTPRequest.URL.String(), "https://qingstor.com:443/aaa/bbb/ccc-ddd?upload_id=0")
assert.Equal(t, r.HTTPRequest.Header.Get("Range"), "100-")
assert.Equal(t, r.HTTPRequest.Header.Get("If-Modified-Since"), "Thu, 01 Sep 2016 15:30:00 GMT")
assert.Equal(t, r.HTTPRequest.Header.Get("Content-Length"), "12")
assert.Equal(t, r.HTTPRequest.Header.Get("Authorization"), "QS ACCESS_KEY_ID:pA7G9qo4iQ6YHu7p4fX9Wcg4V9S6Mcgvz7p/0wEdz78=")
httpResponse := &http.Response{Header: http.Header{}}
httpResponse.StatusCode = 400
httpResponse.Header.Set("Content-Type", "application/json")
responseString := `{
"code": "bad_request",
"message": "Invalid argument(s) or invalid argument value(s)",
"request_id": "1e588695254aa08cf7a43f612e6ce14b",
"url": "http://docs.qingcloud.com/object_storage/api/object/get.html"
}`
httpResponse.Body = ioutil.NopCloser(bytes.NewReader([]byte(responseString)))
assert.Nil(t, err)
r.HTTPResponse = httpResponse
err = r.unpack()
assert.NotNil(t, err)
switch e := err.(type) {
case *errors.QingStorError:
assert.Equal(t, "bad_request", e.Code)
assert.Equal(t, "1e588695254aa08cf7a43f612e6ce14b", e.RequestID)
}
}

View File

@@ -1,127 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package signer
import (
"net/http"
"testing"
"time"
"github.com/pengsrc/go-shared/convert"
"github.com/stretchr/testify/assert"
)
func TestQingStorSignerWriteSignature(t *testing.T) {
url := "https://qingstor.com/?acl&upload_id=fde133b5f6d932cd9c79bac3c7318da1&part_number=0&other=abc"
httpRequest, err := http.NewRequest("GET", url, nil)
httpRequest.Header.Set("Date", convert.TimeToString(time.Time{}, convert.RFC822))
httpRequest.Header.Set("X-QS-Test-2", "Test 2")
httpRequest.Header.Set("X-QS-Test-1", "Test 1")
assert.Nil(t, err)
s := QingStorSigner{
AccessKeyID: "ENV_ACCESS_KEY_ID",
SecretAccessKey: "ENV_SECRET_ACCESS_KEY",
}
err = s.WriteSignature(httpRequest)
assert.Nil(t, err)
signature := "QS ENV_ACCESS_KEY_ID:bvglZF9iMOv1RaCTxPYWxexmt1UN2m5WKngYnhDEp2c="
assert.Equal(t, signature, httpRequest.Header.Get("Authorization"))
}
func TestQingStorSignerWriteSignatureWithXQSDate(t *testing.T) {
url := "https://qingstor.com/?acl&upload_id=fde133b5f6d932cd9c79bac3c7318da1&part_number=0&other=abc"
httpRequest, err := http.NewRequest("GET", url, nil)
httpRequest.Header.Set("Date", convert.TimeToString(time.Time{}, convert.RFC822))
httpRequest.Header.Set("X-QS-Date", convert.TimeToString(time.Time{}, convert.RFC822))
httpRequest.Header.Set("X-QS-Test-2", "Test 2")
httpRequest.Header.Set("X-QS-Test-1", "Test 1")
assert.Nil(t, err)
s := QingStorSigner{
AccessKeyID: "ENV_ACCESS_KEY_ID",
SecretAccessKey: "ENV_SECRET_ACCESS_KEY",
}
err = s.WriteSignature(httpRequest)
assert.Nil(t, err)
signature := "QS ENV_ACCESS_KEY_ID:qkY+tOMdqfDAVv+ZBtlWeEBxlbyIKaQmj5lQlylENzo="
assert.Equal(t, signature, httpRequest.Header.Get("Authorization"))
}
func TestQingStorSignerWriteSignatureChinese(t *testing.T) {
url := "https://zone.qingstor.com/bucket-name/中文"
httpRequest, err := http.NewRequest("GET", url, nil)
httpRequest.Header.Set("Date", convert.TimeToString(time.Time{}, convert.RFC822))
assert.Nil(t, err)
s := QingStorSigner{
AccessKeyID: "ENV_ACCESS_KEY_ID",
SecretAccessKey: "ENV_SECRET_ACCESS_KEY",
}
err = s.WriteSignature(httpRequest)
assert.Nil(t, err)
signature := "QS ENV_ACCESS_KEY_ID:XsTXX50kzqBf92zLG1aIUIJmZ0hqIHoaHgkumwnV3fs="
assert.Equal(t, signature, httpRequest.Header.Get("Authorization"))
}
func TestQingStorSignerWriteQuerySignature(t *testing.T) {
url := "https://qingstor.com/?acl&upload_id=fde133b5f6d932cd9c79bac3c7318da1&part_number=0"
httpRequest, err := http.NewRequest("GET", url, nil)
httpRequest.Header.Set("Date", convert.TimeToString(time.Time{}, convert.RFC822))
httpRequest.Header.Set("X-QS-Test-2", "Test 2")
httpRequest.Header.Set("X-QS-Test-1", "Test 1")
assert.Nil(t, err)
s := QingStorSigner{
AccessKeyID: "ENV_ACCESS_KEY_ID",
SecretAccessKey: "ENV_SECRET_ACCESS_KEY",
}
err = s.WriteQuerySignature(httpRequest, 3600)
assert.Nil(t, err)
targetURL := "https://qingstor.com/?acl&upload_id=fde133b5f6d932cd9c79bac3c7318da1&part_number=0&access_key_id=ENV_ACCESS_KEY_ID&expires=3600&signature=GRL3p3NOgHR9CQygASvyo344vdnO1hFke6ZvQ5mDVHM="
assert.Equal(t, httpRequest.URL.String(), targetURL)
}
func TestQingStorSignerWriteQuerySignatureWithXQSDate(t *testing.T) {
url := "https://qingstor.com/?acl&upload_id=fde133b5f6d932cd9c79bac3c7318da1&part_number=0"
httpRequest, err := http.NewRequest("GET", url, nil)
httpRequest.Header.Set("Date", convert.TimeToString(time.Time{}, convert.RFC822))
httpRequest.Header.Set("X-QS-Date", convert.TimeToString(time.Time{}, convert.RFC822))
httpRequest.Header.Set("X-QS-Test-2", "Test 2")
httpRequest.Header.Set("X-QS-Test-1", "Test 1")
assert.Nil(t, err)
s := QingStorSigner{
AccessKeyID: "ENV_ACCESS_KEY_ID",
SecretAccessKey: "ENV_SECRET_ACCESS_KEY",
}
err = s.WriteQuerySignature(httpRequest, 3600)
assert.Nil(t, err)
targetURL := "https://qingstor.com/?acl&upload_id=fde133b5f6d932cd9c79bac3c7318da1&part_number=0&access_key_id=ENV_ACCESS_KEY_ID&expires=3600&signature=plFxMFP1EzKVtdF%2BbApT8rhW9AUAIWfmZcOGH3m27t0="
assert.Equal(t, httpRequest.URL.String(), targetURL)
}

View File

@@ -1,85 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package unpacker
import (
"bytes"
"io/ioutil"
"net/http"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/yunify/qingstor-sdk-go/request/data"
)
func StringValue(v *string) string {
if v != nil {
return *v
}
return ""
}
func IntValue(v *int) int {
if v != nil {
return *v
}
return 0
}
func Int64Value(v *int64) int64 {
if v != nil {
return *v
}
return 0
}
func TimeValue(v *time.Time) time.Time {
if v != nil {
return *v
}
return time.Time{}
}
func TestBaseUnpacker_UnpackHTTPRequest(t *testing.T) {
type FakeOutput struct {
StatusCode *int
A *string `location:"elements" json:"a" name:"a"`
B *string `location:"elements" json:"b" name:"b"`
CD *int `location:"elements" json:"cd" name:"cd"`
EF *int64 `location:"elements" json:"ef" name:"ef"`
}
httpResponse := &http.Response{Header: http.Header{}}
httpResponse.StatusCode = 200
httpResponse.Header.Set("Content-Type", "application/json")
responseString := `{"a": "el_a", "b": "el_b", "cd": 1024, "ef": 2048}`
httpResponse.Body = ioutil.NopCloser(bytes.NewReader([]byte(responseString)))
output := &FakeOutput{}
outputValue := reflect.ValueOf(output)
unpacker := BaseUnpacker{}
err := unpacker.UnpackHTTPRequest(&data.Operation{}, httpResponse, &outputValue)
assert.Nil(t, err)
assert.Equal(t, 200, IntValue(output.StatusCode))
assert.Equal(t, "el_a", StringValue(output.A))
assert.Equal(t, "el_b", StringValue(output.B))
assert.Equal(t, 1024, IntValue(output.CD))
assert.Equal(t, int64(2048), Int64Value(output.EF))
}

View File

@@ -1,150 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package unpacker
import (
"bytes"
"io/ioutil"
"net/http"
"reflect"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/yunify/qingstor-sdk-go/request/data"
"github.com/yunify/qingstor-sdk-go/request/errors"
)
func TestQingStorUnpacker_UnpackHTTPRequest(t *testing.T) {
type Bucket struct {
// Created time of the Bucket
Created *time.Time `json:"created" name:"created" format:"RFC 822"`
// QingCloud Zone ID
Location *string `json:"location" name:"location"`
// Bucket name
Name *string `json:"name" name:"name"`
// URL to access the Bucket
URL *string `json:"url" name:"url"`
}
type ListBucketsOutput struct {
StatusCode *int `location:"statusCode"`
Error *errors.QingStorError
RequestID *string `location:"requestID"`
XTestHeader *string `json:"X-Test-Header" name:"X-Test-Header" location:"headers"`
XTestTime *time.Time `json:"X-Test-Time" name:"X-Test-Time" format:"RFC 822" location:"headers"`
// Buckets information
Buckets []*Bucket `json:"buckets" name:"buckets"`
// Bucket count
Count *int `json:"count" name:"count"`
}
httpResponse := &http.Response{Header: http.Header{
"X-Test-Header": []string{"test-header"},
"X-Test-Time": []string{"Thu, 01 Sep 2016 07:30:00 GMT"},
}}
httpResponse.StatusCode = 200
httpResponse.Header.Set("Content-Type", "application/json")
responseString := `{
"count": 2,
"buckets": [
{
"name": "test-bucket",
"location": "pek3a",
"url": "https://test-bucket.pek3a.qingstor.com",
"created": "2015-07-11T04:45:57Z"
},
{
"name": "test-photos",
"location": "pek3a",
"url": "https://test-photos.pek3a.qingstor.com",
"created": "2015-07-12T09:40:32Z"
}
]
}`
httpResponse.Body = ioutil.NopCloser(bytes.NewReader([]byte(responseString)))
output := &ListBucketsOutput{}
outputValue := reflect.ValueOf(output)
unpacker := QingStorUnpacker{}
err := unpacker.UnpackHTTPRequest(&data.Operation{}, httpResponse, &outputValue)
assert.Nil(t, err)
assert.Equal(t, "test-header", StringValue(output.XTestHeader))
assert.Equal(t, time.Date(2016, 9, 1, 7, 30, 0, 0, time.UTC), TimeValue(output.XTestTime))
assert.Equal(t, 2, IntValue(output.Count))
assert.Equal(t, "test-bucket", StringValue(output.Buckets[0].Name))
assert.Equal(t, "pek3a", StringValue(output.Buckets[0].Location))
assert.Equal(t, time.Date(2015, 7, 12, 9, 40, 32, 0, time.UTC), TimeValue(output.Buckets[1].Created))
}
func TestQingStorUnpacker_UnpackHTTPRequestWithError(t *testing.T) {
type ListBucketsOutput struct {
StatusCode *int `location:"statusCode"`
Error *errors.QingStorError
RequestID *string `location:"requestID"`
}
httpResponse := &http.Response{Header: http.Header{}}
httpResponse.StatusCode = 400
httpResponse.Header.Set("Content-Type", "application/json")
responseString := `{
"code": "bad_request",
"message": "Invalid argument(s) or invalid argument value(s)",
"request_id": "aa08cf7a43f611e5886952542e6ce14b",
"url": "http://docs.qingcloud.com/object_storage/api/bucket/get.html"
}`
httpResponse.Body = ioutil.NopCloser(bytes.NewReader([]byte(responseString)))
output := &ListBucketsOutput{}
outputValue := reflect.ValueOf(output)
unpacker := QingStorUnpacker{}
err := unpacker.UnpackHTTPRequest(&data.Operation{}, httpResponse, &outputValue)
assert.NotNil(t, err)
switch e := err.(type) {
case *errors.QingStorError:
assert.Equal(t, "bad_request", e.Code)
assert.Equal(t, "aa08cf7a43f611e5886952542e6ce14b", e.RequestID)
}
}
func TestQingStorUnpacker_UnpackHeadHTTPRequestWithError(t *testing.T) {
type HeadBucketsOutput struct {
StatusCode *int `location:"statusCode"`
Error *errors.QingStorError
RequestID *string `location:"requestID"`
}
httpResponse := &http.Response{Header: http.Header{}}
httpResponse.StatusCode = 404
httpResponse.Header.Set("Content-Type", "application/json")
httpResponse.Header.Set("X-QS-Request-ID", "aa08cf7a43f611e5886952542e6ce14b")
httpResponse.Body = ioutil.NopCloser(strings.NewReader(""))
output := &HeadBucketsOutput{}
outputValue := reflect.ValueOf(output)
unpacker := QingStorUnpacker{}
err := unpacker.UnpackHTTPRequest(&data.Operation{}, httpResponse, &outputValue)
assert.NotNil(t, err)
switch e := err.(type) {
case *errors.QingStorError:
assert.Equal(t, "aa08cf7a43f611e5886952542e6ce14b", e.RequestID)
}
}

View File

@@ -1,311 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package service
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
var testCasesStringSlice = [][]string{
{"a", "b", "c", "d", "e"},
{"a", "b", "", "", "e"},
}
func TestStringSlice(t *testing.T) {
for idx, in := range testCasesStringSlice {
if in == nil {
continue
}
out := StringSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
}
out2 := StringValueSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
}
}
var testCasesStringValueSlice = [][]*string{
{String("a"), String("b"), nil, String("c")},
}
func TestStringValueSlice(t *testing.T) {
for idx, in := range testCasesStringValueSlice {
if in == nil {
continue
}
out := StringValueSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
if in[i] == nil {
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
} else {
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
}
}
out2 := StringSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
for i := range out2 {
if in[i] == nil {
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
} else {
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
}
}
}
}
var testCasesStringMap = []map[string]string{
{"a": "1", "b": "2", "c": "3"},
}
func TestStringMap(t *testing.T) {
for idx, in := range testCasesStringMap {
if in == nil {
continue
}
out := StringMap(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
}
out2 := StringValueMap(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
}
}
var testCasesBoolSlice = [][]bool{
{true, true, false, false},
}
func TestBoolSlice(t *testing.T) {
for idx, in := range testCasesBoolSlice {
if in == nil {
continue
}
out := BoolSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
}
out2 := BoolValueSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
}
}
var testCasesBoolValueSlice = [][]*bool{}
func TestBoolValueSlice(t *testing.T) {
for idx, in := range testCasesBoolValueSlice {
if in == nil {
continue
}
out := BoolValueSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
if in[i] == nil {
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
} else {
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
}
}
out2 := BoolSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
for i := range out2 {
if in[i] == nil {
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
} else {
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
}
}
}
}
var testCasesBoolMap = []map[string]bool{
{"a": true, "b": false, "c": true},
}
func TestBoolMap(t *testing.T) {
for idx, in := range testCasesBoolMap {
if in == nil {
continue
}
out := BoolMap(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
}
out2 := BoolValueMap(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
}
}
var testCasesIntSlice = [][]int{
{1, 2, 3, 4},
}
func TestIntSlice(t *testing.T) {
for idx, in := range testCasesIntSlice {
if in == nil {
continue
}
out := IntSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
}
out2 := IntValueSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
}
}
var testCasesIntValueSlice = [][]*int{}
func TestIntValueSlice(t *testing.T) {
for idx, in := range testCasesIntValueSlice {
if in == nil {
continue
}
out := IntValueSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
if in[i] == nil {
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
} else {
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
}
}
out2 := IntSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
for i := range out2 {
if in[i] == nil {
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
} else {
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
}
}
}
}
var testCasesIntMap = []map[string]int{
{"a": 3, "b": 2, "c": 1},
}
func TestIntMap(t *testing.T) {
for idx, in := range testCasesIntMap {
if in == nil {
continue
}
out := IntMap(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
}
out2 := IntValueMap(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
}
}
var testCasesTimeSlice = [][]time.Time{
{time.Now(), time.Now().AddDate(100, 0, 0)},
}
func TestTimeSlice(t *testing.T) {
for idx, in := range testCasesTimeSlice {
if in == nil {
continue
}
out := TimeSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
}
out2 := TimeValueSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
}
}
var testCasesTimeValueSlice = [][]*time.Time{}
func TestTimeValueSlice(t *testing.T) {
for idx, in := range testCasesTimeValueSlice {
if in == nil {
continue
}
out := TimeValueSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
if in[i] == nil {
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
} else {
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
}
}
out2 := TimeSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
for i := range out2 {
if in[i] == nil {
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
} else {
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
}
}
}
}
var testCasesTimeMap = []map[string]time.Time{
{"a": time.Now().AddDate(-100, 0, 0), "b": time.Now()},
}
func TestTimeMap(t *testing.T) {
for idx, in := range testCasesTimeMap {
if in == nil {
continue
}
out := TimeMap(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
}
out2 := TimeValueMap(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
}
}

View File

@@ -1,8 +0,0 @@
{
"output": {
"file_naming": {
"style": "snake_case",
"extension": ".go"
}
}
}

View File

@@ -1,44 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
{{$service := .Data.Service}}
// Package service provides {{$service.Name}} Service API (API Version {{$service.APIVersion}})
package service
import (
"net/http"
"github.com/yunify/qingstor-sdk-go/config"
"github.com/yunify/qingstor-sdk-go/request"
"github.com/yunify/qingstor-sdk-go/request/data"
)
var _ http.Header
{{if $service.Description}}// Service {{$service.Description}}{{end}}
type Service struct {
Config *config.Config
}
// Init initializes a new service.
func Init(c *config.Config) (*Service, error) {
return &Service{Config: c}, nil
}
{{range $_, $operation := $service.Operations}}
{{template "RenderOperation" passThrough $service $operation}}
{{end}}

View File

@@ -1,402 +0,0 @@
{{define "Type"}}
{{- $typeName := index . 0 -}}
{{- $disablePointer := index . 1 -}}
{{- if eq $typeName "string" -}}
{{- if not $disablePointer -}}*{{- end -}}string
{{- else if eq $typeName "boolean" -}}
{{- if not $disablePointer -}}*{{- end -}}bool
{{- else if eq $typeName "integer" -}}
{{- if not $disablePointer -}}*{{- end -}}int
{{- else if eq $typeName "long" -}}
{{- if not $disablePointer -}}*{{- end -}}int64
{{- else if eq $typeName "timestamp" -}}
{{- if not $disablePointer -}}*{{- end -}}time.Time
{{- else if eq $typeName "binary" -}}
io.Reader
{{- else if eq $typeName "array" -}}
interface{}
{{- else if eq $typeName "object" -}}
interface{}
{{- else if eq $typeName "map" -}}
interface{}
{{- else if eq $typeName "any" -}}
interface{}
{{- else -}}
*{{$typeName | camelCase}}Type
{{- end -}}
{{end}}
{{define "PropertyType"}}
{{- $property := index . 0 -}}
{{- $disablePointer := index . 1 -}}
{{- if eq $property.Type "object" -}}
{{template "Type" passThrough $property.ExtraType $disablePointer}}
{{- else if eq $property.Type "array" -}}
[]{{template "Type" passThrough $property.ExtraType $disablePointer}}
{{- else if eq $property.Type "map" -}}
map[string]{{template "Type" passThrough $property.ExtraType $disablePointer}}
{{- else if eq $property.Type "any" -}}
{{template "Type" passThrough $property.Type $disablePointer}}
{{- else -}}
{{template "Type" passThrough $property.Type $disablePointer}}
{{- end -}}
{{end}}
{{define "PropertyTags"}}
{{- $property := . -}}
{{- if $property.IsRequired -}}
{{- printf `json:"%s"` ($property.Name | normalized) -}}
{{- else -}}
{{- printf `json:"%s,omitempty"` ($property.Name | normalized) -}}
{{- end -}}
{{- printf ` name:"%s"` ($property.Name | normalized) -}}
{{- if $property.Format}}
{{- printf ` format:"%s"` $property.Format -}}
{{- end -}}
{{- if $property.Default -}}
{{- printf ` default:"%s"` $property.Default -}}
{{- end -}}
{{end}}
{{define "PropertyTagsDashConnected"}}
{{- $property := . -}}
{{- printf `json:"%s"` ($property.Name | dashConnected) -}}
{{- printf ` name:"%s"` ($property.Name | dashConnected) -}}
{{end}}
{{define "PropertyExtraTags"}}
{{- $propertyExtraTags := . -}}
{{- if $propertyExtraTags -}}
{{- printf " %s" $propertyExtraTags -}}
{{- end -}}
{{end}}
{{define "RenderProperties"}}
{{- $customizedType := index . 0 -}}
{{- $propertyExtraTags := index . 1 -}}
{{- $operationName := index . 2 -}}
{{range $_, $property := $customizedType.Properties -}}
{{if or (ne $operationName "Delete Multiple Objects") (ne $property.ID "Content-MD5") -}}
{{if $property.Description -}}
// {{$property.Description}}
{{end -}}
{{if $property.Enum -}}
// {{$property.ID | camelCase}}'s available values: {{$property.Enum | commaConnected}}
{{end -}}
{{$property.ID | camelCase | upperFirst}}{{" " -}}
{{template "PropertyType" passThrough $property false}}{{" " -}}
`{{template "PropertyTags" $property}}{{template "PropertyExtraTags" $propertyExtraTags}}`{{" " -}}
{{if $property.IsRequired -}}
// Required
{{- end}}
{{- end}}
{{end}}
{{end}}
{{define "RenderOperation"}}
{{$service := index . 0}}
{{$operation := index . 1}}
{{$belongs := replace $service.Name "QingStor" "Service" -1}}
{{$belongs := replace $belongs "Object" "Bucket" -1}}
{{$opID := $operation.ID | camelCase}}
{{$isBucket := eq $service.Name "Bucket"}}
{{$isObject := eq $service.Name "Object"}}
{{$hasQuery := gt (len $operation.Request.Query.Properties) 0}}
{{$hasHeaders := gt (len $operation.Request.Headers.Properties) 0}}
{{$hasElements := gt (len $operation.Request.Elements.Properties) 0}}
{{$hasStringBody := eq $operation.Request.Body.Type "string"}}
{{$hasBinaryBody := eq $operation.Request.Body.Type "binary"}}
{{$hasInput := or $hasQuery $hasHeaders $hasElements $hasStringBody $hasBinaryBody}}
{{if $operation.Description -}}
{{if eq $belongs "Bucket" -}}
// {{replace $opID "Bucket" "" -1}} does {{$operation.Description}}
{{else -}}
// {{$opID}} does {{$operation.Description}}
{{end -}}
{{end -}}
{{if $operation.DocumentationURL -}}
// Documentation URL: {{$operation.DocumentationURL}}
{{- end}}
{{if eq $belongs "Bucket" -}}
func (s *{{$belongs}}) {{replace $opID "Bucket" "" -1 -}}(
{{- if $isObject}}objectKey string,{{end -}}
{{- if $hasInput}}input *{{$opID}}Input{{end -}}
) (*{{$opID}}Output, error) {
{{else -}}
func (s *{{$belongs}}) {{$opID}}(
{{- if $hasInput}}input *{{$opID}}Input{{end -}}
) (*{{$opID}}Output, error) {
{{end -}}
{{if eq $belongs "Bucket" -}}
r, x, err := s.{{replace $opID "Bucket" "" -1}}Request(
{{- if $isObject}}objectKey,{{end -}}
{{- if $hasInput}}input{{end -}}
)
{{else -}}
r, x, err := s.{{$opID}}Request(
{{- if $hasInput}}input{{end -}}
)
{{end}}
if err != nil {
return x, err
}
err = r.Send()
if err != nil {
return nil, err
}
requestID := r.HTTPResponse.Header.Get(http.CanonicalHeaderKey("X-QS-Request-ID"))
x.RequestID = &requestID
return x, err
}
{{if $operation.Description -}}
{{if eq $belongs "Bucket" -}}
// {{replace $opID "Bucket" "" -1}}Request creates request and output object of {{$opID}}.
{{else -}}
// {{$opID}}Request creates request and output object of {{$opID}}.
{{end -}}
{{end -}}
{{if eq $belongs "Bucket" -}}
func (s *{{$belongs}}) {{replace $opID "Bucket" "" -1 -}}Request(
{{- if $isObject}}objectKey string,{{end -}}
{{- if $hasInput}}input *{{$opID}}Input{{end -}}
) (*request.Request, *{{$opID}}Output, error) {
{{else -}}
func (s *{{$belongs}}) {{$opID}}Request(
{{- if $hasInput}}input *{{$opID}}Input{{end -}}
) (*request.Request, *{{$opID}}Output, error) {
{{end -}}
{{if $hasInput}}
if input == nil {
input = &{{$opID}}Input{}
}
{{end}}
{{$path := $operation.Request.Path}}
{{$path := replace $path "{" "<" -1}}
{{$path := replace $path "}" ">" -1}}
{{$path := dashConnected $path}}
{{- if ne $belongs "Service"}}
properties := *s.Properties
{{- end}}
{{if eq $service.Name "Object"}}
properties.ObjectKey = &objectKey
{{end}}
o := &data.Operation{
Config: s.Config,
{{- if ne $belongs "Service"}}
Properties: &properties,
{{- end}}
APIName: "{{$operation.Name}}",
RequestMethod: "{{$operation.Request.Method}}",
RequestURI: "{{$path}}",
StatusCodes: []int{
{{range $keyStatus, $valueStatus := $operation.Responses -}}
{{- if $valueStatus.StatusCode -}}
{{$valueStatus.StatusCode.Code}}, // {{$valueStatus.StatusCode.Description}}
{{else}}
200, // OK
{{end -}}
{{else}}
200, // OK
{{end -}}
},
}
x := &{{$opID}}Output{}
r, err := request.New(o, {{if $hasInput}}input{{else}}nil{{end}}, x)
if err != nil {
return nil, nil, err
}
return r, x, nil
}
{{if $hasInput}}
// {{$opID}}Input presents input for {{$opID}}.
type {{$opID}}Input struct {
{{- if $operation.Request.Query.Properties | len}}
{{$data := $operation.Request.Query -}}
{{template "RenderProperties" passThrough $data `location:"query"` $operation.Name}}
{{end}}
{{- if $operation.Request.Headers.Properties | len}}
{{$data := $operation.Request.Headers -}}
{{template "RenderProperties" passThrough $data `location:"headers"` $operation.Name}}
{{end}}
{{- if $operation.Request.Elements.Properties | len}}
{{$data := $operation.Request.Elements -}}
{{template "RenderProperties" passThrough $data `location:"elements"` $operation.Name}}
{{end}}
{{- if eq $operation.Request.Body.Type "string"}}
{{if $operation.Request.Body.Description -}}
// {{$operation.Request.Body.Description}}
{{- end}}
Body string `location:"body"`
{{else if eq $operation.Request.Body.Type "binary"}}
{{if $operation.Request.Body.Description -}}
// {{$operation.Request.Body.Description}}
{{- end}}
Body io.Reader `location:"body"`
{{end}}
}
// Validate validates the input for {{$opID}}.
func (v *{{$opID}}Input) Validate() error {
{{template "ValidateCustomizedType" passThrough $operation.Request.Query $operation.Name}}
{{template "ValidateCustomizedType" passThrough $operation.Request.Headers $operation.Name}}
{{template "ValidateCustomizedType" passThrough $operation.Request.Elements $operation.Name}}
return nil
}
{{end}}
// {{$opID}}Output presents output for {{$opID}}.
type {{$opID}}Output struct {
StatusCode *int `location:"statusCode"`
RequestID *string `location:"requestID"`
{{range $keyStatus, $valueStatus := $operation.Responses -}}
{{if eq $valueStatus.Body.Type "string"}}
{{if $valueStatus.Body.Description -}}
// {{$valueStatus.Body.Description}}
{{- end}}
Body string `location:"body"`
{{else if eq $valueStatus.Body.Type "binary"}}
{{if $valueStatus.Body.Description -}}
// {{$valueStatus.Body.Description}}
{{- end}}
Body io.ReadCloser `location:"body"`
{{end}}
{{if $valueStatus.Elements.Properties | len}}
{{$data := $valueStatus.Elements}}
{{template "RenderProperties" passThrough $data `location:"elements"` $operation.Name}}
{{end}}
{{if $valueStatus.Headers.Properties | len}}
{{$data := $valueStatus.Headers}}
{{template "RenderProperties" passThrough $data `location:"headers"` $operation.Name}}
{{end}}
{{end}}
}
{{range $keyStatus, $valueStatus := $operation.Responses -}}
{{if eq $valueStatus.Body.Type "binary"}}
// Close will close the underlay body.
func (o *{{$opID}}Output) Close() (err error) {
if o.Body != nil {
return o.Body.Close()
}
return
}
{{end}}
{{end}}
{{end}}
{{define "SubServiceInitParams"}}
{{- $customizedType := index . 0 -}}
{{- $disablePointer := index . 1 -}}
{{- range $_, $property := $customizedType.Properties -}}
{{$property.ID | camelCase | lowerFirstWord}}{{" " -}}
{{template "PropertyType" passThrough $property $disablePointer}},
{{- end -}}
{{end}}
{{define "ValidateCustomizedType"}}
{{$customizedType := index . 0}}
{{$operationName := index . 1}}
{{range $_, $property := $customizedType.Properties}}
{{if or (ne $operationName "Delete Multiple Objects") (ne $property.ID "Content-MD5") -}}
{{$isNormalType := or (eq $property.Type "string") (eq $property.Type "integer")}}
{{$isContentLength := eq $property.ID "Content-Length"}}
{{if and $isNormalType (not $isContentLength) }}
{{if $property.IsRequired }}
if v.{{$property.ID | camelCase}} == nil {
return errors.ParameterRequiredError{
ParameterName: "{{$property.ID | camelCase}}",
ParentName: "{{$customizedType.ID | camelCase}}",
}
}
{{end}}
{{$parameterName := $property.ID | camelCase | lowerFirstWord}}
{{if gt ($property.Enum | len) 0}}
if v.{{$property.ID | camelCase}} != nil {
{{$parameterName}}ValidValues := []string{
{{- $property.Enum | commaConnectedWithQuote -}}
}
{{$parameterName}}ParameterValue := fmt.Sprint(*v.{{$property.ID | camelCase}})
{{$parameterName}}IsValid := false
for _, value := range {{$parameterName}}ValidValues {
if value == {{$parameterName}}ParameterValue {
{{$parameterName}}IsValid = true
}
}
if !{{$parameterName}}IsValid {
return errors.ParameterValueNotAllowedError{
ParameterName: "{{$property.ID | camelCase}}",
ParameterValue: {{$parameterName}}ParameterValue,
AllowedValues: {{$parameterName}}ValidValues,
}
}
}
{{end}}
{{end}}
{{if eq $property.Type "object"}}
if v.{{$property.ID | camelCase}} != nil {
if err := v.{{$property.ID | camelCase}}.Validate(); err != nil {
return err
}
}
{{if $property.IsRequired }}
if v.{{$property.ID | camelCase}} == nil {
return errors.ParameterRequiredError{
ParameterName: "{{$property.ID | camelCase}}",
ParentName: "{{$customizedType.ID | camelCase}}",
}
}
{{end}}
{{end}}
{{if eq $property.Type "array"}}
{{if $property.IsRequired}}
if len(v.{{$property.ID | camelCase}}) == 0 {
return errors.ParameterRequiredError{
ParameterName: "{{$property.ID | camelCase}}",
ParentName: "{{$customizedType.ID | camelCase}}",
}
}
{{end}}
{{$isNotString := ne $property.ExtraType "string"}}
{{$isNotInteger := ne $property.ExtraType "integer"}}
{{$isNotTimestamp := ne $property.ExtraType "timestamp"}}
{{if and $isNotString $isNotInteger $isNotTimestamp}}
if len(v.{{$property.ID | camelCase}}) > 0 {
for _, property := range v.{{$property.ID | camelCase}} {
if err := property.Validate(); err != nil {
return err
}
}
}
{{end}}
{{end}}
{{end}}
{{end}}
{{end}}

View File

@@ -1,70 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
{{$service := .Data.Service}}
{{$subService := index .Data.SubServices .CurrentSubServiceID}}
package service
import (
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/yunify/qingstor-sdk-go/config"
"github.com/yunify/qingstor-sdk-go/request"
"github.com/yunify/qingstor-sdk-go/request/data"
"github.com/yunify/qingstor-sdk-go/request/errors"
)
var _ fmt.State
var _ io.Reader
var _ http.Header
var _ strings.Reader
var _ time.Time
var _ config.Config
{{if ne $subService.Name "Object"}}
// {{$subService.ID | camelCase}} presents {{$subService.ID | snakeCase}}.
type {{$subService.ID | camelCase}} struct {
Config *config.Config
Properties *Properties
}
// {{$subService.ID | camelCase}} initializes a new {{$subService.ID | snakeCase}}.
func (s *Service) {{$subService.ID | camelCase}}(
{{- template "SubServiceInitParams" passThrough $subService.Properties true -}}
) (*{{$subService.ID | camelCase}}, error) {
{{- range $_, $property := $subService.Properties.Properties -}}
{{if eq $property.ID "zone"}}
{{$property.ID}} = strings.ToLower({{$property.ID}})
{{end}}
{{- end -}}
properties := &Properties{
{{range $_, $property := $subService.Properties.Properties -}}
{{$property.ID | upperFirst}}: &{{$property.ID}},
{{end -}}
}
return &{{$subService.ID | camelCase}}{Config: s.Config, Properties: properties}, nil
}
{{end}}
{{range $_, $operation := $subService.Operations}}
{{template "RenderOperation" passThrough $subService $operation}}
{{end}}

View File

@@ -1,63 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
{{$service := .Data.Service}}
{{$objectSubService := index .Data.SubServices "Object"}}
{{$customizedTypes := .Data.CustomizedTypes}}
package service
import (
"fmt"
"time"
"github.com/yunify/qingstor-sdk-go/request/errors"
)
// Properties presents the service properties.
type Properties struct {
{{- template "RenderProperties" passThrough $service.Properties "" "" -}}
{{if $objectSubService -}}
{{range $_, $p := $objectSubService.Properties.Properties -}}
{{- if $p.Description -}}
// {{$p.Description}}
{{end -}}
{{if $p.Enum -}}
// {{camelCase $p.ID}}'s available values: {{commaConnected $p.Enum}}
{{end -}}
{{$p.ID | camelCase | upperFirst}}{{" " -}}
{{template "PropertyType" passThrough $p false}}{{" " -}}
`{{template "PropertyTagsDashConnected" $p}}`{{" " -}}
{{if $p.IsRequired -}}
// Required
{{- end}}
{{end}}
{{end}}
}
{{range $_, $customizedType := $customizedTypes}}
// {{$customizedType.ID | camelCase}}Type presents {{$customizedType.ID | camelCase}}.
type {{$customizedType.ID | camelCase}}Type struct {
{{template "RenderProperties" passThrough $customizedType "" ""}}
}
// Validate validates the {{$customizedType.ID | camelCase}}.
func (v *{{$customizedType.ID | camelCase}}Type) Validate() error {
{{template "ValidateCustomizedType" passThrough $customizedType ""}}
return nil
}
{{end}}

View File

@@ -1,3 +0,0 @@
# Ignore local config
config.yaml
test_config.yaml

View File

@@ -1,269 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package main
import (
"encoding/json"
"errors"
"fmt"
"github.com/DATA-DOG/godog"
"github.com/DATA-DOG/godog/gherkin"
qsErrors "github.com/yunify/qingstor-sdk-go/request/errors"
qs "github.com/yunify/qingstor-sdk-go/service"
)
// BucketFeatureContext provides feature context for bucket.
func BucketFeatureContext(s *godog.Suite) {
s.Step(`^initialize the bucket$`, initializeTheBucket)
s.Step(`^the bucket is initialized$`, theBucketIsInitialized)
s.Step(`^put bucket$`, putBucketFake)
s.Step(`^put bucket status code is (\d+)$`, putBucketStatusCodeIsFake)
s.Step(`^put same bucket again$`, putSameBucketAgain)
s.Step(`^put same bucket again status code is (\d+)$$`, putSameBucketAgainStatusCodeIs)
s.Step(`^list objects$`, listObjects)
s.Step(`^list objects status code is (\d+)$`, listObjectsStatusCodeIs)
s.Step(`^list objects keys count is (\d+)$`, listObjectsKeysCountIs)
s.Step(`^head bucket$`, headBucket)
s.Step(`^head bucket status code is (\d+)$`, headBucketStatusCodeIs)
s.Step(`^delete bucket$`, deleteBucketFake)
s.Step(`^delete bucket status code is (\d+)$`, deleteBucketStatusCodeIsFake)
s.Step(`^delete multiple objects:$`, deleteMultipleObjects)
s.Step(`^delete multiple objects code is (\d+)$`, deleteMultipleObjectsCodeIs)
s.Step(`^get bucket statistics$`, getBucketStatistics)
s.Step(`^get bucket statistics status code is (\d+)$`, getBucketStatisticsStatusCodeIs)
s.Step(`^get bucket statistics status is "([^"]*)"$`, getBucketStatisticsStatusIs)
s.Step(`^an object created by initiate multipart upload$`, anObjectCreatedByInitiateMultipartUpload)
s.Step(`^list multipart uploads$`, listMultipartUploads)
s.Step(`^list multipart uploads count is (\d+)$`, listMultipartUploadsCountIs)
s.Step(`^list multipart uploads with prefix$`, listMultipartUploadsWithPrefix)
s.Step(`^list multipart uploads with prefix count is (\d+)$`, listMultipartUploadsWithPrefixCountIs)
}
// --------------------------------------------------------------------------
var bucket *qs.Bucket
func initializeTheBucket() error {
bucket, err = qsService.Bucket(tc.BucketName, tc.Zone)
return err
}
func theBucketIsInitialized() error {
if bucket == nil {
return errors.New("Bucket is not initialized")
}
return nil
}
// --------------------------------------------------------------------------
var putBucketOutput *qs.PutBucketOutput
func putBucket() error {
putBucketOutput, err = bucket.Put()
return err
}
func putBucketFake() error {
return nil
}
func putBucketStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(putBucketOutput.StatusCode), statusCode)
}
func putBucketStatusCodeIsFake(_ int) error {
return nil
}
// --------------------------------------------------------------------------
func putSameBucketAgain() error {
_, err = bucket.Put()
return nil
}
func putSameBucketAgainStatusCodeIs(statusCode int) error {
switch e := err.(type) {
case *qsErrors.QingStorError:
return checkEqual(e.StatusCode, statusCode)
}
return fmt.Errorf("put same bucket again should get \"%d\"", statusCode)
}
// --------------------------------------------------------------------------
var listObjectsOutput *qs.ListObjectsOutput
func listObjects() error {
listObjectsOutput, err = bucket.ListObjects(&qs.ListObjectsInput{
Delimiter: qs.String("/"),
Limit: qs.Int(1000),
Prefix: qs.String("Test/"),
Marker: qs.String("Next"),
})
return err
}
func listObjectsStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(listObjectsOutput.StatusCode), statusCode)
}
func listObjectsKeysCountIs(count int) error {
return checkEqual(len(listObjectsOutput.Keys), count)
}
// --------------------------------------------------------------------------
var headBucketOutput *qs.HeadBucketOutput
func headBucket() error {
headBucketOutput, err = bucket.Head()
return err
}
func headBucketStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(headBucketOutput.StatusCode), statusCode)
}
// --------------------------------------------------------------------------
var deleteBucketOutput *qs.DeleteBucketOutput
func deleteBucket() error {
deleteBucketOutput, err = bucket.Delete()
return err
}
func deleteBucketFake() error {
return nil
}
func deleteBucketStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(deleteBucketOutput.StatusCode), statusCode)
}
func deleteBucketStatusCodeIsFake(_ int) error {
return nil
}
// --------------------------------------------------------------------------
var deleteMultipleObjectsOutput *qs.DeleteMultipleObjectsOutput
func deleteMultipleObjects(requestJSON *gherkin.DocString) error {
_, err := bucket.PutObject("object_0", nil)
if err != nil {
return err
}
_, err = bucket.PutObject("object_1", nil)
if err != nil {
return err
}
_, err = bucket.PutObject("object_2", nil)
if err != nil {
return err
}
deleteMultipleObjectsInput := &qs.DeleteMultipleObjectsInput{}
err = json.Unmarshal([]byte(requestJSON.Content), deleteMultipleObjectsInput)
if err != nil {
return err
}
deleteMultipleObjectsOutput, err = bucket.DeleteMultipleObjects(
&qs.DeleteMultipleObjectsInput{
Objects: deleteMultipleObjectsInput.Objects,
Quiet: deleteMultipleObjectsInput.Quiet,
},
)
return err
}
func deleteMultipleObjectsCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(deleteMultipleObjectsOutput.StatusCode), statusCode)
}
// --------------------------------------------------------------------------
var getBucketStatisticsOutput *qs.GetBucketStatisticsOutput
func getBucketStatistics() error {
getBucketStatisticsOutput, err = bucket.GetStatistics()
return err
}
func getBucketStatisticsStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(getBucketStatisticsOutput.StatusCode), statusCode)
}
func getBucketStatisticsStatusIs(status string) error {
return checkEqual(qs.StringValue(getBucketStatisticsOutput.Status), status)
}
// --------------------------------------------------------------------------
var listMultipartUploadsOutputObjectKey = "list_multipart_uploads_object_key"
var listMultipartUploadsInitiateOutput *qs.InitiateMultipartUploadOutput
var listMultipartUploadsOutput *qs.ListMultipartUploadsOutput
func anObjectCreatedByInitiateMultipartUpload() error {
listMultipartUploadsInitiateOutput, err = bucket.InitiateMultipartUpload(
listMultipartUploadsOutputObjectKey, nil,
)
return err
}
func listMultipartUploads() error {
listMultipartUploadsOutput, err = bucket.ListMultipartUploads(nil)
return err
}
func listMultipartUploadsCountIs(count int) error {
return checkEqual(len(listMultipartUploadsOutput.Uploads), count)
}
func listMultipartUploadsWithPrefix() error {
listMultipartUploadsOutput, err = bucket.ListMultipartUploads(
&qs.ListMultipartUploadsInput{
Prefix: qs.String(listMultipartUploadsOutputObjectKey),
},
)
return err
}
func listMultipartUploadsWithPrefixCountIs(count int) error {
_, err = bucket.AbortMultipartUpload(
listMultipartUploadsOutputObjectKey, &qs.AbortMultipartUploadInput{
UploadID: listMultipartUploadsInitiateOutput.UploadID,
},
)
if err != nil {
return err
}
return checkEqual(len(listMultipartUploadsOutput.Uploads), count)
}

View File

@@ -1,79 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package main
import (
"encoding/json"
"fmt"
"github.com/DATA-DOG/godog"
"github.com/DATA-DOG/godog/gherkin"
qs "github.com/yunify/qingstor-sdk-go/service"
)
// BucketACLFeatureContext provides feature context for bucket ACL.
func BucketACLFeatureContext(s *godog.Suite) {
s.Step(`^put bucket ACL:$`, putBucketACL)
s.Step(`^put bucket ACL status code is (\d+)$`, putBucketACLStatusCodeIs)
s.Step(`^get bucket ACL$`, getBucketACL)
s.Step(`^get bucket ACL status code is (\d+)$`, getBucketACLStatusCodeIs)
s.Step(`^get bucket ACL should have grantee name "([^"]*)"$`, getBucketACLShouldHaveGranteeName)
}
// --------------------------------------------------------------------------
var putBucketACLOutput *qs.PutBucketACLOutput
func putBucketACL(ACLJSONText *gherkin.DocString) error {
putBucketACLInput := &qs.PutBucketACLInput{}
err = json.Unmarshal([]byte(ACLJSONText.Content), putBucketACLInput)
if err != nil {
return err
}
putBucketACLOutput, err = bucket.PutACL(putBucketACLInput)
return err
}
func putBucketACLStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(putBucketACLOutput.StatusCode), statusCode)
}
// --------------------------------------------------------------------------
var getBucketACLOutput *qs.GetBucketACLOutput
func getBucketACL() error {
getBucketACLOutput, err = bucket.GetACL()
return err
}
func getBucketACLStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(getBucketACLOutput.StatusCode), statusCode)
}
func getBucketACLShouldHaveGranteeName(name string) error {
for _, ACL := range getBucketACLOutput.ACL {
if qs.StringValue(ACL.Grantee.Name) == name {
return nil
}
}
return fmt.Errorf("Grantee name \"%s\" not found in bucket ACLs", name)
}

View File

@@ -1,95 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package main
import (
"encoding/json"
"fmt"
"github.com/DATA-DOG/godog"
"github.com/DATA-DOG/godog/gherkin"
qs "github.com/yunify/qingstor-sdk-go/service"
)
// BucketCORSFeatureContext provides feature context for bucket CORS.
func BucketCORSFeatureContext(s *godog.Suite) {
s.Step(`^put bucket CORS:$`, putBucketCORS)
s.Step(`^put bucket CORS status code is (\d+)$`, putBucketCORSStatusCodeIs)
s.Step(`^get bucket CORS$`, getBucketCORS)
s.Step(`^get bucket CORS status code is (\d+)$`, getBucketCORSStatusCodeIs)
s.Step(`^get bucket CORS should have allowed origin "([^"]*)"$`, getBucketCORSShouldHaveAllowedOrigin)
s.Step(`^delete bucket CORS`, deleteBucketCORS)
s.Step(`^delete bucket CORS status code is (\d+)$`, deleteBucketCORSStatusCodeIs)
}
// --------------------------------------------------------------------------
var putBucketCORSOutput *qs.PutBucketCORSOutput
func putBucketCORS(CORSJSONText *gherkin.DocString) error {
putBucketCORSInput := &qs.PutBucketCORSInput{}
err = json.Unmarshal([]byte(CORSJSONText.Content), putBucketCORSInput)
if err != nil {
return err
}
putBucketCORSOutput, err = bucket.PutCORS(putBucketCORSInput)
return err
}
func putBucketCORSStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(putBucketCORSOutput.StatusCode), statusCode)
}
// --------------------------------------------------------------------------
var getBucketCORSOutput *qs.GetBucketCORSOutput
func getBucketCORS() error {
getBucketCORSOutput, err = bucket.GetCORS()
return err
}
func getBucketCORSStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(getBucketCORSOutput.StatusCode), statusCode)
}
func getBucketCORSShouldHaveAllowedOrigin(origin string) error {
for _, CORSRule := range getBucketCORSOutput.CORSRules {
if qs.StringValue(CORSRule.AllowedOrigin) == origin {
return nil
}
}
return fmt.Errorf("Allowed origin \"%s\" not found in bucket CORS rules", origin)
}
// --------------------------------------------------------------------------
var deleteBucketCORSOutput *qs.DeleteBucketCORSOutput
func deleteBucketCORS() error {
deleteBucketCORSOutput, err = bucket.DeleteCORS()
return err
}
func deleteBucketCORSStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(deleteBucketCORSOutput.StatusCode), statusCode)
}

View File

@@ -1,88 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package main
import (
"encoding/json"
"github.com/DATA-DOG/godog"
"github.com/DATA-DOG/godog/gherkin"
qs "github.com/yunify/qingstor-sdk-go/service"
)
// BucketExternalMirrorFeatureContext provides feature context for bucket external mirror.
func BucketExternalMirrorFeatureContext(s *godog.Suite) {
s.Step(`^put bucket external mirror:$`, putBucketExternalMirror)
s.Step(`^put bucket external mirror status code is (\d+)$`, putBucketExternalMirrorStatusCodeIs)
s.Step(`^get bucket external mirror$`, getBucketExternalMirror)
s.Step(`^get bucket external mirror status code is (\d+)$`, getBucketExternalMirrorStatusCodeIs)
s.Step(`^get bucket external mirror should have source_site "([^"]*)"$`, getBucketExternalMirrorShouldHaveSourceSite)
s.Step(`^delete bucket external mirror$`, deleteBucketExternalMirror)
s.Step(`^delete bucket external mirror status code is (\d+)$`, deleteBucketExternalMirrorStatusCodeIs)
}
// --------------------------------------------------------------------------
var putBucketExternalMirrorOutput *qs.PutBucketExternalMirrorOutput
func putBucketExternalMirror(ExternalMirrorJSONText *gherkin.DocString) error {
putBucketExternalMirrorInput := &qs.PutBucketExternalMirrorInput{}
err = json.Unmarshal([]byte(ExternalMirrorJSONText.Content), putBucketExternalMirrorInput)
if err != nil {
return err
}
putBucketExternalMirrorOutput, err = bucket.PutExternalMirror(putBucketExternalMirrorInput)
return err
}
func putBucketExternalMirrorStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(putBucketExternalMirrorOutput.StatusCode), statusCode)
}
// --------------------------------------------------------------------------
var getBucketExternalMirrorOutput *qs.GetBucketExternalMirrorOutput
func getBucketExternalMirror() error {
getBucketExternalMirrorOutput, err = bucket.GetExternalMirror()
return err
}
func getBucketExternalMirrorStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(getBucketExternalMirrorOutput.StatusCode), statusCode)
}
func getBucketExternalMirrorShouldHaveSourceSite(sourceSite string) error {
return checkEqual(qs.StringValue(getBucketExternalMirrorOutput.SourceSite), sourceSite)
}
// --------------------------------------------------------------------------
var deleteBucketExternalMirrorOutput *qs.DeleteBucketExternalMirrorOutput
func deleteBucketExternalMirror() error {
deleteBucketExternalMirrorOutput, err = bucket.DeleteExternalMirror()
return err
}
func deleteBucketExternalMirrorStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(deleteBucketExternalMirrorOutput.StatusCode), statusCode)
}

View File

@@ -1,105 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package main
import (
"encoding/json"
"fmt"
"github.com/DATA-DOG/godog"
"github.com/DATA-DOG/godog/gherkin"
qs "github.com/yunify/qingstor-sdk-go/service"
)
// BucketPolicyFeatureContext provides feature context for bucket policy.
func BucketPolicyFeatureContext(s *godog.Suite) {
s.Step(`^put bucket policy:$`, putBucketPolicy)
s.Step(`^put bucket policy status code is (\d+)$`, putBucketPolicyStatusCodeIs)
s.Step(`^get bucket policy$`, getBucketPolicy)
s.Step(`^get bucket policy status code is (\d+)$`, getBucketPolicyStatusCodeIs)
s.Step(`^get bucket policy should have Referer "([^"]*)"$`, getBucketPolicyShouldHaveReferer)
s.Step(`^delete bucket policy$`, deleteBucketPolicy)
s.Step(`^delete bucket policy status code is (\d+)$`, deleteBucketPolicyStatusCodeIs)
}
// --------------------------------------------------------------------------
var putBucketPolicyOutput *qs.PutBucketPolicyOutput
func putBucketPolicy(PolicyJSONText *gherkin.DocString) error {
putBucketPolicyInput := &qs.PutBucketPolicyInput{}
err = json.Unmarshal([]byte(PolicyJSONText.Content), putBucketPolicyInput)
if err != nil {
return err
}
if len(putBucketPolicyInput.Statement) == 1 {
putBucketPolicyInput.Statement[0].Resource = qs.StringSlice([]string{tc.BucketName + "/*"})
}
putBucketPolicyOutput, err = bucket.PutPolicy(putBucketPolicyInput)
return err
}
func putBucketPolicyStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(putBucketPolicyOutput.StatusCode), statusCode)
}
// --------------------------------------------------------------------------
var getBucketPolicyOutput *qs.GetBucketPolicyOutput
func getBucketPolicy() error {
getBucketPolicyOutput, err = bucket.GetPolicy()
return err
}
func getBucketPolicyStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(getBucketPolicyOutput.StatusCode), statusCode)
}
func getBucketPolicyShouldHaveReferer(compare string) error {
for _, statement := range getBucketPolicyOutput.Statement {
if statement.Condition != nil &&
statement.Condition.StringLike != nil {
for _, referer := range statement.Condition.StringLike.Referer {
if qs.StringValue(referer) == compare {
return nil
}
}
}
}
return fmt.Errorf("Referer \"%s\" not found in bucket policy statement", compare)
}
// --------------------------------------------------------------------------
var deleteBucketPolicyOutput *qs.DeleteBucketPolicyOutput
func deleteBucketPolicy() error {
deleteBucketPolicyOutput, err = bucket.DeletePolicy()
return err
}
func deleteBucketPolicyStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(deleteBucketPolicyOutput.StatusCode), statusCode)
}

View File

@@ -1,15 +0,0 @@
# QingStor services configuration
access_key_id: ACCESS_KEY_ID
secret_access_key: SECRET_ACCESS_KEY
host: qingstor.com
port: 443
protocol: https
connection_retries: 3
# Additional User-Agent
additional_user_agent: "test/integration"
# Valid log levels are "debug", "info", "warn", "error", and "fatal".
log_level: debug

View File

@@ -1,9 +0,0 @@
hash: a498153ecdb880e9d2a81849ffae311e03bf11570e30d28f64d396d532a066c6
updated: 2017-05-22T13:59:32.427609656+08:00
imports:
- name: github.com/DATA-DOG/godog
version: 623ff9946e5c72834c19a019a153b8e3a338a52c
subpackages:
- colors
- gherkin
testImports: []

View File

@@ -1,4 +0,0 @@
package: github.com/yunify/qingstor-sdk-go/test
import:
- package: github.com/DATA-DOG/godog
version: v0.6.0

View File

@@ -1,68 +0,0 @@
package main
import (
"errors"
"os"
"path"
"github.com/DATA-DOG/godog"
qs "github.com/yunify/qingstor-sdk-go/service"
)
// ImageFeatureContext provides feature context for image.
func ImageFeatureContext(s *godog.Suite) {
s.Step(`^image process with key "([^"]*)" and query "([^"]*)"$`, imageProcessWithKeyAndQuery)
s.Step(`^image process status code is (\d+)$`, imageProcessStatusCodeIs)
}
var imageName string
func imageProcessWithKeyAndQuery(objectKey, query string) error {
if bucket == nil {
return errors.New("The bucket is not exist")
}
file, err := os.Open(path.Join("features", "fixtures", objectKey))
if err != nil {
return err
}
defer file.Close()
imageName = objectKey
_, err = bucket.PutObject(imageName, &qs.PutObjectInput{Body: file})
if err != nil {
return err
}
output, err := bucket.ImageProcess(objectKey, &qs.ImageProcessInput{
Action: &query})
if err != nil {
return err
}
imageProcessOutput = output
return nil
}
var imageProcessOutput *qs.ImageProcessOutput
func imageProcessStatusCodeIs(statusCode int) error {
defer deleteImage(imageName)
return checkEqual(qs.IntValue(imageProcessOutput.StatusCode), statusCode)
}
var oOutput *qs.DeleteObjectOutput
func deleteImage(imageName string) error {
if bucket == nil {
return errors.New("The bucket is not exist")
}
oOutput, err = bucket.DeleteObject(imageName)
if err != nil {
return err
}
return checkEqual(qs.IntValue(oOutput.StatusCode), 204)
}

View File

@@ -1,10 +0,0 @@
@upload
Feature: the upload feature
Scenario: create the uploader
When initialize uploader
Then uploader is initialized
Scenario: upload large file
When upload a large file
Then the large file is uploaded

View File

@@ -1,142 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package main
import (
"gopkg.in/yaml.v2"
"io/ioutil"
"os"
"time"
"github.com/DATA-DOG/godog"
"github.com/yunify/qingstor-sdk-go/config"
qsErrors "github.com/yunify/qingstor-sdk-go/request/errors"
qs "github.com/yunify/qingstor-sdk-go/service"
)
func main() {
setUp()
context := func(s *godog.Suite) {
ServiceFeatureContext(s)
BucketFeatureContext(s)
BucketACLFeatureContext(s)
BucketCORSFeatureContext(s)
BucketPolicyFeatureContext(s)
BucketExternalMirrorFeatureContext(s)
ObjectFeatureContext(s)
ObjectMultipartFeatureContext(s)
ImageFeatureContext(s)
UploadFeatureContext(s)
}
options := godog.Options{
Format: "pretty",
Paths: []string{"./features", "./local_features"},
Tags: "",
}
status := godog.RunWithOptions("*", context, options)
//tearDown()
os.Exit(status)
}
func setUp() {
loadTestConfig()
loadConfig()
initQingStorService()
err := initializeTheBucket()
checkErrorForExit(err)
err = theBucketIsInitialized()
checkErrorForExit(err)
//err = putBucket()
//checkError(err)
//err = putBucketStatusCodeIs(201)
//checkError(err)
}
func tearDown() {
err := deleteBucket()
checkError(err)
err = deleteBucketStatusCodeIs(204)
checkError(err)
retries := 0
for retries < tc.MaxRetries {
deleteBucketOutput, err = bucket.Delete()
checkError(err)
if err != nil {
switch e := err.(type) {
case *qsErrors.QingStorError:
if e.Code == "bucket_not_exists" {
return
}
}
}
retries++
time.Sleep(time.Second * time.Duration(tc.RetryWaitTime))
}
}
var err error
var tc *testConfig
var c *config.Config
var qsService *qs.Service
type testConfig struct {
Zone string `json:"zone" yaml:"zone"`
BucketName string `json:"bucket_name" yaml:"bucket_name"`
RetryWaitTime int `json:"retry_wait_time" yaml:"retry_wait_time"`
MaxRetries int `json:"max_retries" yaml:"max_retries"`
Concurrency int `json:"concurrency"`
}
func loadTestConfig() {
if tc == nil {
configYAML, err := ioutil.ReadFile("./test_config.yaml")
checkErrorForExit(err)
tc = &testConfig{}
err = yaml.Unmarshal(configYAML, tc)
checkErrorForExit(err)
}
}
func loadConfig() {
if c == nil {
c, err = config.NewDefault()
checkErrorForExit(err)
err = c.LoadConfigFromFilePath("./config.yaml")
checkErrorForExit(err)
}
}
func initQingStorService() {
if qsService == nil {
qsService, err = qs.Init(c)
checkErrorForExit(err)
}
}

View File

@@ -1,647 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package main
import (
"bytes"
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"sync"
"github.com/DATA-DOG/godog"
"github.com/yunify/qingstor-sdk-go/request"
qs "github.com/yunify/qingstor-sdk-go/service"
)
// ObjectFeatureContext provides feature context for object.
func ObjectFeatureContext(s *godog.Suite) {
s.Step(`^put object with key "(.{1,})"$`, putObjectWithKey)
s.Step(`^put object status code is (\d+)$`, putObjectStatusCodeIs)
s.Step(`^copy object with key "(.{1,})"$`, copyObjectWithKey)
s.Step(`^copy object status code is (\d+)$`, copyObjectStatusCodeIs)
s.Step(`^move object with key "(.{1,})"$`, moveObjectWithKey)
s.Step(`^move object status code is (\d+)$`, moveObjectStatusCodeIs)
s.Step(`^get object with key "(.{1,})"$`, getObjectWithKey)
s.Step(`^get object status code is (\d+)$`, getObjectStatusCodeIs)
s.Step(`^get object content length is (\d+)$`, getObjectContentLengthIs)
s.Step(`^get object "(.{1,})" with content type "(.{1,})"$`, getObjectWithContentType)
s.Step(`^get object content type is "(.{1,})"$`, getObjectContentTypeIs)
s.Step(`^get object "(.{1,})" with query signature$`, getObjectWithQuerySignature)
s.Step(`^get object with query signature content length is (\d+)$`, getObjectWithQuerySignatureContentLengthIs)
s.Step(`^head object with key "(.{1,})"$`, headObjectWithKey)
s.Step(`^head object status code is (\d+)$`, headObjectStatusCodeIs)
s.Step(`^options object "(.{1,})" with method "([^"]*)" and origin "([^"]*)"$`, optionsObjectWithMethodAndOrigin)
s.Step(`^options object status code is (\d+)$`, optionsObjectStatusCodeIs)
s.Step(`^delete object with key "(.{1,})"$`, deleteObjectWithKey)
s.Step(`^delete object status code is (\d+)$`, deleteObjectStatusCodeIs)
s.Step(`^delete the move object with key "(.{1,})"$`, deleteTheMoveObjectWithKey)
s.Step(`^delete the move object status code is (\d+)$`, deleteTheMoveObjectStatusCodeIs)
}
// --------------------------------------------------------------------------
var putObjectOutputs []*qs.PutObjectOutput
func putObjectWithKey(objectKey string) error {
_, err = exec.Command("dd", "if=/dev/zero", "of=/tmp/sdk_bin", "bs=1024", "count=1").Output()
if err != nil {
return err
}
defer os.Remove("/tmp/sdk_bin")
errChan := make(chan error, tc.Concurrency)
putObjectOutputs = make([]*qs.PutObjectOutput, tc.Concurrency)
wg := sync.WaitGroup{}
wg.Add(tc.Concurrency)
for i := 0; i < tc.Concurrency; i++ {
go func(index int, errChan chan<- error) {
wg.Done()
file, err := os.Open("/tmp/sdk_bin")
if err != nil {
errChan <- err
return
}
defer file.Close()
hash := md5.New()
_, err = io.Copy(hash, file)
if err != nil {
errChan <- err
return
}
hashInBytes := hash.Sum(nil)[:16]
md5String := hex.EncodeToString(hashInBytes)
//file.Seek(0, io.SeekStart)
file.Seek(0, 0)
if len(objectKey) > 1000 {
objectKey = objectKey[:1000]
}
putObjectOutput, err := bucket.PutObject(
fmt.Sprintf("%s-%d", objectKey, index),
&qs.PutObjectInput{
ContentType: qs.String("text/plain"),
ContentMD5: qs.String(md5String),
Body: file,
},
)
if err != nil {
errChan <- err
return
}
putObjectOutputs[index] = putObjectOutput
errChan <- nil
return
}(i, errChan)
}
wg.Wait()
for i := 0; i < tc.Concurrency; i++ {
err = <-errChan
if err != nil {
return err
}
}
return nil
}
func putObjectStatusCodeIs(statusCode int) error {
for _, output := range putObjectOutputs {
err = checkEqual(qs.IntValue(output.StatusCode), statusCode)
if err != nil {
return err
}
}
return nil
}
// --------------------------------------------------------------------------
var copyObjectOutputs []*qs.PutObjectOutput
func copyObjectWithKey(objectKey string) error {
errChan := make(chan error, tc.Concurrency)
copyObjectOutputs = make([]*qs.PutObjectOutput, tc.Concurrency)
wg := sync.WaitGroup{}
wg.Add(tc.Concurrency)
for i := 0; i < tc.Concurrency; i++ {
go func(index int, errChan chan<- error) {
wg.Done()
if len(objectKey) > 1000 {
objectKey = objectKey[:1000]
}
copyObjectOutput, err := bucket.PutObject(
fmt.Sprintf("%s-%d-copy", objectKey, index),
&qs.PutObjectInput{
XQSCopySource: qs.String(
fmt.Sprintf("/%s/%s-%d", tc.BucketName, objectKey, index),
),
})
if err != nil {
errChan <- err
return
}
copyObjectOutputs[index] = copyObjectOutput
errChan <- nil
return
}(i, errChan)
}
wg.Wait()
for i := 0; i < tc.Concurrency; i++ {
err = <-errChan
if err != nil {
return err
}
}
return nil
}
func copyObjectStatusCodeIs(statusCode int) error {
for _, output := range copyObjectOutputs {
err = checkEqual(qs.IntValue(output.StatusCode), statusCode)
if err != nil {
return err
}
}
return nil
}
// --------------------------------------------------------------------------
var moveObjectOutputs []*qs.PutObjectOutput
func moveObjectWithKey(objectKey string) error {
errChan := make(chan error, tc.Concurrency)
moveObjectOutputs = make([]*qs.PutObjectOutput, tc.Concurrency)
wg := sync.WaitGroup{}
wg.Add(tc.Concurrency)
for i := 0; i < tc.Concurrency; i++ {
go func(index int, errChan chan<- error) {
wg.Done()
if len(objectKey) > 1000 {
objectKey = objectKey[:1000]
}
moveObjectOutput, err := bucket.PutObject(
fmt.Sprintf("%s-%d-move", objectKey, index),
&qs.PutObjectInput{
XQSMoveSource: qs.String(
fmt.Sprintf(`/%s/%s-%d-copy`, tc.BucketName, objectKey, index),
),
})
if err != nil {
errChan <- err
return
}
moveObjectOutputs[index] = moveObjectOutput
errChan <- nil
return
}(i, errChan)
}
wg.Wait()
for i := 0; i < tc.Concurrency; i++ {
err = <-errChan
if err != nil {
return err
}
}
return nil
}
func moveObjectStatusCodeIs(statusCode int) error {
for _, output := range moveObjectOutputs {
err = checkEqual(qs.IntValue(output.StatusCode), statusCode)
if err != nil {
return err
}
}
return nil
}
// --------------------------------------------------------------------------
var getObjectOutputs []*qs.GetObjectOutput
func getObjectWithKey(objectKey string) error {
errChan := make(chan error, tc.Concurrency)
getObjectOutputs = make([]*qs.GetObjectOutput, tc.Concurrency)
wg := sync.WaitGroup{}
wg.Add(tc.Concurrency)
for i := 0; i < tc.Concurrency; i++ {
go func(index int, errChan chan<- error) {
wg.Done()
if len(objectKey) > 1000 {
objectKey = objectKey[:1000]
}
getObjectOutput, err := bucket.GetObject(
fmt.Sprintf("%s-%d", objectKey, index), nil,
)
if err != nil {
errChan <- err
return
}
getObjectOutputs[index] = getObjectOutput
errChan <- nil
return
}(i, errChan)
}
wg.Wait()
for i := 0; i < tc.Concurrency; i++ {
err = <-errChan
if err != nil {
return err
}
}
return nil
}
func getObjectStatusCodeIs(statusCode int) error {
for _, output := range getObjectOutputs {
err = checkEqual(qs.IntValue(output.StatusCode), statusCode)
if err != nil {
return err
}
}
return nil
}
func getObjectContentLengthIs(length int) error {
buffer := &bytes.Buffer{}
for _, output := range getObjectOutputs {
buffer.Truncate(0)
buffer.ReadFrom(output.Body)
err = checkEqual(len(buffer.Bytes())*1024, length)
if err != nil {
return err
}
}
return nil
}
// --------------------------------------------------------------------------
var getObjectWithContentTypeRequests []*request.Request
func getObjectWithContentType(objectKey, contentType string) error {
errChan := make(chan error, tc.Concurrency)
getObjectWithContentTypeRequests = make([]*request.Request, tc.Concurrency)
wg := sync.WaitGroup{}
wg.Add(tc.Concurrency)
for i := 0; i < tc.Concurrency; i++ {
go func(index int, errChan chan<- error) {
wg.Done()
if len(objectKey) > 1000 {
objectKey = objectKey[:1000]
}
getObjectWithContentTypeRequest, _, err := bucket.GetObjectRequest(
fmt.Sprintf("%s-%d", objectKey, index),
&qs.GetObjectInput{
ResponseContentType: qs.String(contentType),
},
)
if err != nil {
errChan <- err
return
}
err = getObjectWithContentTypeRequest.Send()
if err != nil {
errChan <- err
return
}
err = getObjectWithContentTypeRequest.Send()
if err != nil {
errChan <- err
return
}
getObjectWithContentTypeRequests[index] = getObjectWithContentTypeRequest
errChan <- nil
return
}(i, errChan)
}
wg.Wait()
for i := 0; i < tc.Concurrency; i++ {
err = <-errChan
if err != nil {
return err
}
}
return nil
}
func getObjectContentTypeIs(contentType string) error {
for _, r := range getObjectWithContentTypeRequests {
err = checkEqual(r.HTTPResponse.Header.Get("Content-Type"), contentType)
if err != nil {
return err
}
}
return nil
}
// --------------------------------------------------------------------------
var getObjectWithQuerySignatureURLs []string
func getObjectWithQuerySignature(objectKey string) error {
errChan := make(chan error, tc.Concurrency)
getObjectWithQuerySignatureURLs = make([]string, tc.Concurrency)
wg := sync.WaitGroup{}
wg.Add(tc.Concurrency)
for i := 0; i < tc.Concurrency; i++ {
go func(index int, errChan chan<- error) {
wg.Done()
if len(objectKey) > 1000 {
objectKey = objectKey[:1000]
}
r, _, err := bucket.GetObjectRequest(
fmt.Sprintf("%s-%d", objectKey, index), nil,
)
if err != nil {
errChan <- err
return
}
err = r.Build()
if err != nil {
errChan <- err
return
}
err = r.SignQuery(10)
if err != nil {
errChan <- err
return
}
getObjectWithQuerySignatureURLs[index] = r.HTTPRequest.URL.String()
errChan <- nil
return
}(i, errChan)
}
wg.Wait()
for i := 0; i < tc.Concurrency; i++ {
err = <-errChan
if err != nil {
return err
}
}
return nil
}
func getObjectWithQuerySignatureContentLengthIs(length int) error {
buffer := &bytes.Buffer{}
for _, url := range getObjectWithQuerySignatureURLs {
out, err := http.Get(url)
if err != nil {
return err
}
buffer.Truncate(0)
buffer.ReadFrom(out.Body)
out.Body.Close()
err = checkEqual(len(buffer.Bytes())*1024, length)
if err != nil {
return err
}
}
return nil
}
// --------------------------------------------------------------------------
var headObjectOutputs []*qs.HeadObjectOutput
func headObjectWithKey(objectKey string) error {
errChan := make(chan error, tc.Concurrency)
headObjectOutputs = make([]*qs.HeadObjectOutput, tc.Concurrency)
wg := sync.WaitGroup{}
wg.Add(tc.Concurrency)
for i := 0; i < tc.Concurrency; i++ {
go func(index int, errChan chan<- error) {
wg.Done()
if len(objectKey) > 1000 {
objectKey = objectKey[:1000]
}
headObjectOutput, err := bucket.HeadObject(
fmt.Sprintf("%s-%d", objectKey, index), nil,
)
if err != nil {
errChan <- err
return
}
headObjectOutputs[index] = headObjectOutput
errChan <- nil
return
}(i, errChan)
}
wg.Wait()
for i := 0; i < tc.Concurrency; i++ {
err = <-errChan
if err != nil {
return err
}
}
return nil
}
func headObjectStatusCodeIs(statusCode int) error {
for _, output := range headObjectOutputs {
err = checkEqual(qs.IntValue(output.StatusCode), statusCode)
if err != nil {
return err
}
}
return nil
}
// --------------------------------------------------------------------------
var optionsObjectOutputs []*qs.OptionsObjectOutput
func optionsObjectWithMethodAndOrigin(objectKey, method, origin string) error {
errChan := make(chan error, tc.Concurrency)
optionsObjectOutputs = make([]*qs.OptionsObjectOutput, tc.Concurrency)
wg := sync.WaitGroup{}
wg.Add(tc.Concurrency)
for i := 0; i < tc.Concurrency; i++ {
go func(index int, errChan chan<- error) {
wg.Done()
if len(objectKey) > 1000 {
objectKey = objectKey[:1000]
}
optionsObjectOutput, err := bucket.OptionsObject(
fmt.Sprintf("%s-%d", objectKey, index),
&qs.OptionsObjectInput{
AccessControlRequestMethod: qs.String(method),
Origin: qs.String(origin),
},
)
if err != nil {
errChan <- err
return
}
optionsObjectOutputs[index] = optionsObjectOutput
errChan <- nil
return
}(i, errChan)
}
wg.Wait()
for i := 0; i < tc.Concurrency; i++ {
err = <-errChan
if err != nil {
return err
}
}
return nil
}
func optionsObjectStatusCodeIs(statusCode int) error {
for _, output := range optionsObjectOutputs {
err = checkEqual(qs.IntValue(output.StatusCode), statusCode)
if err != nil {
return err
}
}
return nil
}
// --------------------------------------------------------------------------
var deleteObjectOutputs []*qs.DeleteObjectOutput
var deleteTheMoveObjectOutputs []*qs.DeleteObjectOutput
func deleteObjectWithKey(objectKey string) error {
errChan := make(chan error, tc.Concurrency)
deleteObjectOutputs = make([]*qs.DeleteObjectOutput, tc.Concurrency)
wg := sync.WaitGroup{}
wg.Add(tc.Concurrency)
for i := 0; i < tc.Concurrency; i++ {
go func(index int, errChan chan<- error) {
wg.Done()
if len(objectKey) > 1000 {
objectKey = objectKey[:1000]
}
deleteObjectOutput, err := bucket.DeleteObject(
fmt.Sprintf("%s-%d", objectKey, index),
)
if err != nil {
errChan <- err
return
}
deleteObjectOutputs[index] = deleteObjectOutput
errChan <- nil
return
}(i, errChan)
}
wg.Wait()
for i := 0; i < tc.Concurrency; i++ {
err = <-errChan
if err != nil {
return err
}
}
return nil
}
func deleteObjectStatusCodeIs(statusCode int) error {
for _, output := range deleteObjectOutputs {
err = checkEqual(qs.IntValue(output.StatusCode), statusCode)
if err != nil {
return err
}
}
return nil
}
func deleteTheMoveObjectWithKey(objectKey string) error {
errChan := make(chan error, tc.Concurrency)
deleteTheMoveObjectOutputs = make([]*qs.DeleteObjectOutput, tc.Concurrency)
wg := sync.WaitGroup{}
wg.Add(tc.Concurrency)
for i := 0; i < tc.Concurrency; i++ {
go func(index int, errChan chan<- error) {
wg.Done()
if len(objectKey) > 1000 {
objectKey = objectKey[:1000]
}
deleteTheMoveObjectOutput, err := bucket.DeleteObject(
fmt.Sprintf("%s-%d-move", objectKey, index),
)
if err != nil {
errChan <- err
return
}
deleteTheMoveObjectOutputs[index] = deleteTheMoveObjectOutput
errChan <- nil
return
}(i, errChan)
}
wg.Wait()
for i := 0; i < tc.Concurrency; i++ {
err = <-errChan
if err != nil {
return err
}
}
return nil
}
func deleteTheMoveObjectStatusCodeIs(statusCode int) error {
for _, output := range deleteTheMoveObjectOutputs {
err = checkEqual(qs.IntValue(output.StatusCode), statusCode)
if err != nil {
return err
}
}
return nil
}

View File

@@ -1,238 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package main
import (
"os"
"os/exec"
"github.com/DATA-DOG/godog"
"fmt"
"github.com/yunify/qingstor-sdk-go/request/errors"
qs "github.com/yunify/qingstor-sdk-go/service"
)
// ObjectMultipartFeatureContext provides feature context for object multipart.
func ObjectMultipartFeatureContext(s *godog.Suite) {
s.Step(`^initiate multipart upload with key "(.{1,})"$`, initiateMultipartUploadWithKey)
s.Step(`^initiate multipart upload status code is (\d+)$`, initiateMultipartUploadStatusCodeIs)
s.Step(`^upload the first part with key "(.{1,})"$`, uploadTheFirstPartWithKey)
s.Step(`^upload the first part status code is (\d+)$`, uploadTheFirstPartStatusCodeIs)
s.Step(`^upload the second part with key "(.{1,})"$`, uploadTheSecondPartWithKey)
s.Step(`^upload the second part status code is (\d+)$`, uploadTheSecondPartStatusCodeIs)
s.Step(`^upload the third part with key "(.{1,})"$`, uploadTheThirdPartWithKey)
s.Step(`^upload the third part status code is (\d+)$`, uploadTheThirdPartStatusCodeIs)
s.Step(`^list multipart with key "(.{1,})"$`, listMultipartWithKey)
s.Step(`^list multipart status code is (\d+)$`, listMultipartStatusCodeIs)
s.Step(`^list multipart object parts count is (\d+)$`, listMultipartObjectPartsCountIs)
s.Step(`^complete multipart upload with key "(.{1,})"$`, completeMultipartUploadWithKey)
s.Step(`^complete multipart upload status code is (\d+)$`, completeMultipartUploadStatusCodeIs)
s.Step(`^abort multipart upload with key "(.{1,})"$`, abortMultipartUploadWithKey)
s.Step(`^abort multipart upload status code is (\d+)$`, abortMultipartUploadStatusCodeIs)
s.Step(`^delete the multipart object with key "(.{1,})"$`, deleteTheMultipartObjectWithKey)
s.Step(`^delete the multipart object status code is (\d+)$`, deleteTheMultipartObjectStatusCodeIs)
}
// --------------------------------------------------------------------------
var initiateMultipartUploadOutput *qs.InitiateMultipartUploadOutput
func initiateMultipartUploadWithKey(objectKey string) error {
initiateMultipartUploadOutput, err = bucket.InitiateMultipartUpload(
objectKey,
&qs.InitiateMultipartUploadInput{
ContentType: qs.String("text/plain"),
},
)
return err
}
func initiateMultipartUploadStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(initiateMultipartUploadOutput.StatusCode), statusCode)
}
// --------------------------------------------------------------------------
var uploadTheFirstPartOutput *qs.UploadMultipartOutput
var uploadTheSecondPartOutput *qs.UploadMultipartOutput
var uploadTheThirdPartOutput *qs.UploadMultipartOutput
func uploadTheFirstPartWithKey(objectKey string) error {
_, err = exec.Command("dd", "if=/dev/zero", "of=/tmp/sdk_bin_part_0", "bs=1048576", "count=5").Output()
if err != nil {
return err
}
defer os.Remove("/tmp/sdk_bin_part_0")
file, err := os.Open("/tmp/sdk_bin_part_0")
if err != nil {
return err
}
defer file.Close()
uploadTheFirstPartOutput, err = bucket.UploadMultipart(
objectKey,
&qs.UploadMultipartInput{
UploadID: initiateMultipartUploadOutput.UploadID,
PartNumber: qs.Int(0),
Body: file,
},
)
return err
}
func uploadTheFirstPartStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(uploadTheFirstPartOutput.StatusCode), statusCode)
}
func uploadTheSecondPartWithKey(objectKey string) error {
_, err = exec.Command("dd", "if=/dev/zero", "of=/tmp/sdk_bin_part_1", "bs=1048576", "count=4").Output()
if err != nil {
return err
}
defer os.Remove("/tmp/sdk_bin_part_1")
file, err := os.Open("/tmp/sdk_bin_part_1")
if err != nil {
return err
}
defer file.Close()
uploadTheSecondPartOutput, err = bucket.UploadMultipart(
objectKey,
&qs.UploadMultipartInput{
UploadID: initiateMultipartUploadOutput.UploadID,
PartNumber: qs.Int(1),
Body: file,
},
)
return err
}
func uploadTheSecondPartStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(uploadTheSecondPartOutput.StatusCode), statusCode)
}
func uploadTheThirdPartWithKey(objectKey string) error {
_, err = exec.Command("dd", "if=/dev/zero", "of=/tmp/sdk_bin_part_2", "bs=1048576", "count=3").Output()
if err != nil {
return err
}
defer os.Remove("/tmp/sdk_bin_part_2")
file, err := os.Open("/tmp/sdk_bin_part_2")
if err != nil {
return err
}
defer file.Close()
uploadTheThirdPartOutput, err = bucket.UploadMultipart(
objectKey,
&qs.UploadMultipartInput{
UploadID: initiateMultipartUploadOutput.UploadID,
PartNumber: qs.Int(2),
Body: file,
},
)
return err
}
func uploadTheThirdPartStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(uploadTheThirdPartOutput.StatusCode), statusCode)
}
// --------------------------------------------------------------------------
var listMultipartOutput *qs.ListMultipartOutput
func listMultipartWithKey(objectKey string) error {
listMultipartOutput, err = bucket.ListMultipart(
objectKey,
&qs.ListMultipartInput{
UploadID: initiateMultipartUploadOutput.UploadID,
},
)
return err
}
func listMultipartStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(listMultipartOutput.StatusCode), statusCode)
}
func listMultipartObjectPartsCountIs(count int) error {
return checkEqual(len(listMultipartOutput.ObjectParts), count)
}
// --------------------------------------------------------------------------
var completeMultipartUploadOutput *qs.CompleteMultipartUploadOutput
func completeMultipartUploadWithKey(objectKey string) error {
completeMultipartUploadOutput, err = bucket.CompleteMultipartUpload(
objectKey,
&qs.CompleteMultipartUploadInput{
UploadID: initiateMultipartUploadOutput.UploadID,
ETag: qs.String(`"4072783b8efb99a9e5817067d68f61c6"`),
ObjectParts: listMultipartOutput.ObjectParts,
},
)
return err
}
func completeMultipartUploadStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(completeMultipartUploadOutput.StatusCode), statusCode)
}
// --------------------------------------------------------------------------
func abortMultipartUploadWithKey(objectKey string) error {
_, err = bucket.AbortMultipartUpload(
objectKey,
&qs.AbortMultipartUploadInput{
UploadID: initiateMultipartUploadOutput.UploadID,
},
)
return nil
}
func abortMultipartUploadStatusCodeIs(statusCode int) error {
switch e := err.(type) {
case *errors.QingStorError:
return checkEqual(e.StatusCode, statusCode)
}
return fmt.Errorf("abort multipart upload should get \"%d\"", statusCode)
}
// --------------------------------------------------------------------------
var deleteTheMultipartObjectOutput *qs.DeleteObjectOutput
func deleteTheMultipartObjectWithKey(objectKey string) error {
deleteTheMultipartObjectOutput, err = bucket.DeleteObject(objectKey)
return err
}
func deleteTheMultipartObjectStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(deleteTheMultipartObjectOutput.StatusCode), statusCode)
}

View File

@@ -1,60 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package main
import (
"errors"
"github.com/DATA-DOG/godog"
qs "github.com/yunify/qingstor-sdk-go/service"
)
// ServiceFeatureContext provides feature context for service.
func ServiceFeatureContext(s *godog.Suite) {
s.Step(`^initialize QingStor service$`, initializeQingStorService)
s.Step(`^the QingStor service is initialized$`, theQingStorServiceIsInitialized)
s.Step(`^list buckets$`, listBuckets)
s.Step(`^list buckets status code is (\d+)$`, listBucketsStatusCodeIs)
}
// --------------------------------------------------------------------------
func initializeQingStorService() error {
return nil
}
func theQingStorServiceIsInitialized() error {
if qsService == nil {
return errors.New("QingStor service is not initialized")
}
return nil
}
// --------------------------------------------------------------------------
var listBucketsOutput *qs.ListBucketsOutput
func listBuckets() error {
listBucketsOutput, err = qsService.ListBuckets(nil)
return err
}
func listBucketsStatusCodeIs(statusCode int) error {
return checkEqual(qs.IntValue(listBucketsOutput.StatusCode), statusCode)
}

View File

@@ -1,9 +0,0 @@
# Test configurations
zone: pek3a
bucket_name: access-key-id
retry_wait_time: 3 # seconds
max_retries: 60
concurrency: 1

View File

@@ -1,67 +0,0 @@
package main
import (
"errors"
"os"
"os/exec"
"github.com/DATA-DOG/godog"
"github.com/yunify/qingstor-sdk-go/client/upload"
)
var uploader *upload.Uploader
// UploadFeatureContext provides feature context for upload.
func UploadFeatureContext(s *godog.Suite) {
s.Step("initialize uploader$", initializeUploader)
s.Step("uploader is initialized$", uploaderIsInitialized)
s.Step("upload a large file$", uploadLargeFile)
s.Step("the large file is uploaded$", largeFileIsUploaded)
}
var fd *os.File
func initializeUploader() error {
uploadSetup()
PartSize := 4 * 1024 * 1024
fd, err = os.Open("test_file")
if err != nil {
return err
}
uploader = upload.Init(bucket, PartSize)
return nil
}
func uploaderIsInitialized() error {
if uploader == nil {
return errors.New("uploader not initialized")
}
return nil
}
var objectKey string
func uploadLargeFile() error {
objectKey = "test_multipart_upload"
err := uploader.Upload(fd, objectKey)
if err != nil {
return err
}
return nil
}
func largeFileIsUploaded() error {
defer uploadTearDown()
return nil
}
func uploadSetup() {
exec.Command("dd", "if=/dev/zero", "of=test_file", "bs=1024", "count=20480").Output()
}
func uploadTearDown() {
exec.Command("rm", "", "test_file").Output()
}

View File

@@ -1,54 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package main
import (
"fmt"
"os"
"reflect"
)
func checkError(err error) {
if err != nil {
fmt.Println(err.Error())
}
}
func checkErrorForExit(err error, code ...int) {
if err != nil {
exitCode := 1
if len(code) > 0 {
exitCode = code[0]
}
fmt.Println(err.Error(), exitCode)
os.Exit(exitCode)
}
}
func checkEqual(value, shouldBe interface{}) error {
if value == nil || shouldBe == nil {
if value == shouldBe {
return nil
}
} else {
if reflect.DeepEqual(value, shouldBe) {
return nil
}
}
return fmt.Errorf("Value \"%v\" should be \"%v\"", value, shouldBe)
}

View File

@@ -1,30 +0,0 @@
package utils
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestURLQueryEscape(t *testing.T) {
assert.Equal(t, "/", URLQueryEscape("/"))
assert.Equal(t, "%20", URLQueryEscape(" "))
assert.Equal(t,
"%21%40%23%24%25%5E%26%2A%28%29_%2B%7B%7D%7C",
URLQueryEscape("!@#$%^&*()_+{}|"),
)
}
func TestURLQueryUnescape(t *testing.T) {
x, err := URLQueryUnescape("/")
assert.Nil(t, err)
assert.Equal(t, "/", x)
x, err = URLQueryUnescape("%20")
assert.Nil(t, err)
assert.Equal(t, " ", x)
x, err = URLQueryUnescape("%21%40%23%24%25%5E%26%2A%28%29_%2B%7B%7D%7C")
assert.Nil(t, err)
assert.Equal(t, "!@#$%^&*()_+{}|", x)
}

View File

@@ -1,28 +0,0 @@
// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or at:
// |
// | http://www.apache.org/licenses/LICENSE-2.0
// |
// | Unless required by applicable law or agreed to in writing, software
// | distributed under the License is distributed on an "AS IS" BASIS,
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// | See the License for the specific language governing permissions and
// | limitations under the License.
// +-------------------------------------------------------------------------
package sdk
import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
)
func TestVersion(t *testing.T) {
assert.Equal(t, "string", reflect.ValueOf(Version).Type().String())
}