vendor: resync with dep ensure

This commit is contained in:
Nick Craig-Wood 2017-09-23 11:50:14 +01:00
parent 6f2a6dfbc5
commit 6390bb2b09
15 changed files with 899 additions and 37 deletions

8
Gopkg.lock generated
View file

@ -212,10 +212,10 @@
revision = "2df9a531813370438a4d79bfc33e21f58063ed87" revision = "2df9a531813370438a4d79bfc33e21f58063ed87"
[[projects]] [[projects]]
branch = "master"
name = "github.com/spf13/pflag" name = "github.com/spf13/pflag"
packages = ["."] packages = ["."]
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66" revision = "7aff26db30c1be810f9de5038ec5ef96ac41fd7c"
version = "v1.0.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -279,7 +279,7 @@
[[projects]] [[projects]]
name = "google.golang.org/appengine" name = "google.golang.org/appengine"
packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"] packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","log","urlfetch"]
revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a"
version = "v1.0.0" version = "v1.0.0"
@ -292,6 +292,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "e660aa956f1e580bda1ce240cdb673bcbc00bb8f2ef2e77e85d3d941528836f1" inputs-digest = "119fcfb60b243cc6c75c9ebd02ddc913a1492a128cb7bdecb7ef180a0fc5be44"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View file

@ -556,6 +556,10 @@ func UnquoteUsage(flag *Flag) (name string, usage string) {
name = "int" name = "int"
case "uint64": case "uint64":
name = "uint" name = "uint"
case "stringSlice":
name = "strings"
case "intSlice":
name = "ints"
} }
return return

View file

@ -978,12 +978,12 @@ const defaultOutput = ` --A for bootstrapping, allo
--IP ip IP address with no default --IP ip IP address with no default
--IPMask ipMask Netmask address with no default --IPMask ipMask Netmask address with no default
--IPNet ipNet IP network with no default --IPNet ipNet IP network with no default
--Ints intSlice int slice with zero default --Ints ints int slice with zero default
--N int a non-zero int (default 27) --N int a non-zero int (default 27)
--ND1 string[="bar"] a string with NoOptDefVal (default "foo") --ND1 string[="bar"] a string with NoOptDefVal (default "foo")
--ND2 num[=4321] a num with NoOptDefVal (default 1234) --ND2 num[=4321] a num with NoOptDefVal (default 1234)
--StringArray stringArray string array with zero default --StringArray stringArray string array with zero default
--StringSlice stringSlice string slice with zero default --StringSlice strings string slice with zero default
--Z int an int that defaults to zero --Z int an int that defaults to zero
--custom custom custom Value implementation --custom custom custom Value implementation
--customP custom a VarP with default (default 10) --customP custom a VarP with default (default 10)

View file

@ -0,0 +1,259 @@
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

@ -0,0 +1,63 @@
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

@ -0,0 +1,272 @@
# 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)
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

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"reflect" "reflect"
"strconv"
"time" "time"
"github.com/pengsrc/go-shared/convert" "github.com/pengsrc/go-shared/convert"
@ -64,6 +65,27 @@ func New(o *data.Operation, i data.Input, x interface{}) (*Request, error) {
// Send sends API request. // Send sends API request.
// It returns error if error occurred. // It returns error if error occurred.
func (r *Request) Send() error { func (r *Request) Send() error {
err := r.Build()
if err != nil {
return err
}
err = r.Sign()
if err != nil {
return err
}
err = r.Do()
if err != nil {
return err
}
return nil
}
// Build checks and builds the API request.
// It returns error if error occurred.
func (r *Request) Build() error {
err := r.check() err := r.check()
if err != nil { if err != nil {
return err return err
@ -74,12 +96,13 @@ func (r *Request) Send() error {
return err return err
} }
err = r.sign() return nil
if err != nil {
return err
} }
err = r.send() // Do sends and unpacks the API request.
// It returns error if error occurred.
func (r *Request) Do() error {
err := r.send()
if err != nil { if err != nil {
return err return err
} }
@ -95,17 +118,7 @@ func (r *Request) Send() error {
// Sign sign the API request by setting the authorization header. // Sign sign the API request by setting the authorization header.
// It returns error if error occurred. // It returns error if error occurred.
func (r *Request) Sign() error { func (r *Request) Sign() error {
err := r.check() err := r.sign()
if err != nil {
return err
}
err = r.build()
if err != nil {
return err
}
err = r.sign()
if err != nil { if err != nil {
return err return err
} }
@ -116,21 +129,30 @@ func (r *Request) Sign() error {
// SignQuery sign the API request by appending query string. // SignQuery sign the API request by appending query string.
// It returns error if error occurred. // It returns error if error occurred.
func (r *Request) SignQuery(timeoutSeconds int) error { func (r *Request) SignQuery(timeoutSeconds int) error {
err := r.check() err := r.signQuery(int(time.Now().Unix()) + timeoutSeconds)
if err != nil { if err != nil {
return err return err
} }
err = r.build() return nil
if err != nil {
return err
} }
err = r.signQuery(int(time.Now().Unix()) + timeoutSeconds) // ApplySignature applies the Authorization header.
if err != nil { // It returns error if error occurred.
return err func (r *Request) ApplySignature(authorization string) error {
r.HTTPRequest.Header.Set("Authorization", authorization)
return nil
} }
// ApplyQuerySignature applies the query signature.
// It returns error if error occurred.
func (r *Request) ApplyQuerySignature(accessKeyID string, expires int, signature string) error {
queryValue := r.HTTPRequest.URL.Query()
queryValue.Set("access_key_id", accessKeyID)
queryValue.Set("expires", strconv.Itoa(expires))
queryValue.Set("signature", signature)
r.HTTPRequest.URL.RawQuery = queryValue.Encode()
return nil return nil
} }

View file

@ -67,7 +67,7 @@ func Time(v time.Time) *time.Time {
return &v return &v
} }
func TestRequest_Send(t *testing.T) { func TestRequestSend(t *testing.T) {
conf, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY") conf, err := config.New("ACCESS_KEY_ID", "SECRET_ACCESS_KEY")
assert.Nil(t, err) assert.Nil(t, err)
logger.SetLevel("warn") logger.SetLevel("warn")

View file

@ -122,12 +122,16 @@ func (qss *QingStorSigner) BuildQuerySignature(request *http.Request, expires in
// BuildStringToSign build the string to sign. // BuildStringToSign build the string to sign.
func (qss *QingStorSigner) BuildStringToSign(request *http.Request) (string, error) { func (qss *QingStorSigner) BuildStringToSign(request *http.Request) (string, error) {
date := request.Header.Get("Date")
if request.Header.Get("X-QS-Date") != "" {
date = ""
}
stringToSign := fmt.Sprintf( stringToSign := fmt.Sprintf(
"%s\n%s\n%s\n%s\n", "%s\n%s\n%s\n%s\n",
request.Method, request.Method,
request.Header.Get("Content-MD5"), request.Header.Get("Content-MD5"),
request.Header.Get("Content-Type"), request.Header.Get("Content-Type"),
request.Header.Get("Date"), date,
) )
stringToSign += qss.buildCanonicalizedHeaders(request) stringToSign += qss.buildCanonicalizedHeaders(request)
@ -247,6 +251,7 @@ func (qss *QingStorSigner) paramsToSign(key string) bool {
"stats": true, "stats": true,
"upload_id": true, "upload_id": true,
"uploads": true, "uploads": true,
"image": true,
"response-expires": true, "response-expires": true,
"response-cache-control": true, "response-cache-control": true,
"response-content-type": true, "response-content-type": true,

View file

@ -46,6 +46,27 @@ func TestQingStorSignerWriteSignature(t *testing.T) {
assert.Equal(t, signature, httpRequest.Header.Get("Authorization")) 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) { func TestQingStorSignerWriteSignatureChinese(t *testing.T) {
url := "https://zone.qingstor.com/bucket-name/中文" url := "https://zone.qingstor.com/bucket-name/中文"
httpRequest, err := http.NewRequest("GET", url, nil) httpRequest, err := http.NewRequest("GET", url, nil)
@ -83,3 +104,24 @@ func TestQingStorSignerWriteQuerySignature(t *testing.T) {
targetURL := "https://qingstor.com/?acl&upload_id=fde133b5f6d932cd9c79bac3c7318da1&part_number=0&access_key_id=ENV_ACCESS_KEY_ID&expires=3600&signature=GRL3p3NOgHR9CQygASvyo344vdnO1hFke6ZvQ5mDVHM=" 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) 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

@ -744,12 +744,14 @@ func (s *Bucket) ListMultipartUploadsRequest(input *ListMultipartUploadsInput) (
type ListMultipartUploadsInput struct { type ListMultipartUploadsInput struct {
// Put all keys that share a common prefix into a list // Put all keys that share a common prefix into a list
Delimiter *string `json:"delimiter,omitempty" name:"delimiter" location:"params"` Delimiter *string `json:"delimiter,omitempty" name:"delimiter" location:"params"`
// Limit results returned from the first key after key_marker sorted by alphabetical order
KeyMarker *string `json:"key_marker,omitempty" name:"key_marker" location:"params"`
// Results count limit // Results count limit
Limit *int `json:"limit,omitempty" name:"limit" location:"params"` Limit *int `json:"limit,omitempty" name:"limit" location:"params"`
// Limit results to keys that start at this marker
Marker *string `json:"marker,omitempty" name:"marker" location:"params"`
// Limits results to keys that begin with the prefix // Limits results to keys that begin with the prefix
Prefix *string `json:"prefix,omitempty" name:"prefix" location:"params"` Prefix *string `json:"prefix,omitempty" name:"prefix" location:"params"`
// Limit results returned from the first uploading segment after upload_id_marker sorted by the time of upload_id
UploadIDMarker *string `json:"upload_id_marker,omitempty" name:"upload_id_marker" location:"params"`
} }
// Validate validates the input for ListMultipartUploads. // Validate validates the input for ListMultipartUploads.
@ -774,8 +776,10 @@ type ListMultipartUploadsOutput struct {
Marker *string `json:"marker,omitempty" name:"marker" location:"elements"` Marker *string `json:"marker,omitempty" name:"marker" location:"elements"`
// Bucket name // Bucket name
Name *string `json:"name,omitempty" name:"name" location:"elements"` Name *string `json:"name,omitempty" name:"name" location:"elements"`
// The last key in keys list // The last key in uploads list
NextMarker *string `json:"next_marker,omitempty" name:"next_marker" location:"elements"` NextKeyMarker *string `json:"next_key_marker,omitempty" name:"next_key_marker" location:"elements"`
// The last upload_id in uploads list
NextUploadIDMarker *string `json:"next_upload_id_marker,omitempty" name:"next_upload_id_marker" location:"elements"`
// Prefix that specified in request parameters // Prefix that specified in request parameters
Prefix *string `json:"prefix,omitempty" name:"prefix" location:"elements"` Prefix *string `json:"prefix,omitempty" name:"prefix" location:"elements"`
// Multipart uploads // Multipart uploads

View file

@ -467,6 +467,105 @@ type HeadObjectOutput struct {
XQSEncryptionCustomerAlgorithm *string `json:"X-QS-Encryption-Customer-Algorithm,omitempty" name:"X-QS-Encryption-Customer-Algorithm" location:"headers"` XQSEncryptionCustomerAlgorithm *string `json:"X-QS-Encryption-Customer-Algorithm,omitempty" name:"X-QS-Encryption-Customer-Algorithm" location:"headers"`
} }
// ImageProcess does Image process with the action on the object
// Documentation URL: https://docs.qingcloud.com/qingstor/data_process/image_process/index.html
func (s *Bucket) ImageProcess(objectKey string, input *ImageProcessInput) (*ImageProcessOutput, error) {
r, x, err := s.ImageProcessRequest(objectKey, input)
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
}
// ImageProcessRequest creates request and output object of ImageProcess.
func (s *Bucket) ImageProcessRequest(objectKey string, input *ImageProcessInput) (*request.Request, *ImageProcessOutput, error) {
if input == nil {
input = &ImageProcessInput{}
}
properties := *s.Properties
properties.ObjectKey = &objectKey
o := &data.Operation{
Config: s.Config,
Properties: &properties,
APIName: "Image Process",
RequestMethod: "GET",
RequestURI: "/<bucket-name>/<object-key>?image",
StatusCodes: []int{
200, // OK
304, // Not modified
},
}
x := &ImageProcessOutput{}
r, err := request.New(o, input, x)
if err != nil {
return nil, nil, err
}
return r, x, nil
}
// ImageProcessInput presents input for ImageProcess.
type ImageProcessInput struct {
// Image process action
Action *string `json:"action" name:"action" location:"params"` // Required
// Specified the Cache-Control response header
ResponseCacheControl *string `json:"response-cache-control,omitempty" name:"response-cache-control" location:"params"`
// Specified the Content-Disposition response header
ResponseContentDisposition *string `json:"response-content-disposition,omitempty" name:"response-content-disposition" location:"params"`
// Specified the Content-Encoding response header
ResponseContentEncoding *string `json:"response-content-encoding,omitempty" name:"response-content-encoding" location:"params"`
// Specified the Content-Language response header
ResponseContentLanguage *string `json:"response-content-language,omitempty" name:"response-content-language" location:"params"`
// Specified the Content-Type response header
ResponseContentType *string `json:"response-content-type,omitempty" name:"response-content-type" location:"params"`
// Specified the Expires response header
ResponseExpires *string `json:"response-expires,omitempty" name:"response-expires" location:"params"`
// Check whether the object has been modified
IfModifiedSince *time.Time `json:"If-Modified-Since,omitempty" name:"If-Modified-Since" format:"RFC 822" location:"headers"`
}
// Validate validates the input for ImageProcess.
func (v *ImageProcessInput) Validate() error {
if v.Action == nil {
return errors.ParameterRequiredError{
ParameterName: "Action",
ParentName: "ImageProcessInput",
}
}
return nil
}
// ImageProcessOutput presents output for ImageProcess.
type ImageProcessOutput struct {
StatusCode *int `location:"statusCode"`
RequestID *string `location:"requestID"`
// The response body
Body io.ReadCloser `location:"body"`
// Object content length
ContentLength *int64 `json:"Content-Length,omitempty" name:"Content-Length" location:"headers"`
}
// InitiateMultipartUpload does Initial multipart upload on the object. // InitiateMultipartUpload does Initial multipart upload on the object.
// Documentation URL: https://docs.qingcloud.com/qingstor/api/object/initiate_multipart_upload.html // Documentation URL: https://docs.qingcloud.com/qingstor/api/object/initiate_multipart_upload.html
func (s *Bucket) InitiateMultipartUpload(objectKey string, input *InitiateMultipartUploadInput) (*InitiateMultipartUploadOutput, error) { func (s *Bucket) InitiateMultipartUpload(objectKey string, input *InitiateMultipartUploadInput) (*InitiateMultipartUploadOutput, error) {
@ -909,6 +1008,24 @@ type UploadMultipartInput struct {
ContentLength *int64 `json:"Content-Length,omitempty" name:"Content-Length" location:"headers"` ContentLength *int64 `json:"Content-Length,omitempty" name:"Content-Length" location:"headers"`
// Object multipart content MD5sum // Object multipart content MD5sum
ContentMD5 *string `json:"Content-MD5,omitempty" name:"Content-MD5" location:"headers"` ContentMD5 *string `json:"Content-MD5,omitempty" name:"Content-MD5" location:"headers"`
// Specify range of the source object
XQSCopyRange *string `json:"X-QS-Copy-Range,omitempty" name:"X-QS-Copy-Range" location:"headers"`
// Copy source, format (/<bucket-name>/<object-key>)
XQSCopySource *string `json:"X-QS-Copy-Source,omitempty" name:"X-QS-Copy-Source" location:"headers"`
// Encryption algorithm of the object
XQSCopySourceEncryptionCustomerAlgorithm *string `json:"X-QS-Copy-Source-Encryption-Customer-Algorithm,omitempty" name:"X-QS-Copy-Source-Encryption-Customer-Algorithm" location:"headers"`
// Encryption key of the object
XQSCopySourceEncryptionCustomerKey *string `json:"X-QS-Copy-Source-Encryption-Customer-Key,omitempty" name:"X-QS-Copy-Source-Encryption-Customer-Key" location:"headers"`
// MD5sum of encryption key
XQSCopySourceEncryptionCustomerKeyMD5 *string `json:"X-QS-Copy-Source-Encryption-Customer-Key-MD5,omitempty" name:"X-QS-Copy-Source-Encryption-Customer-Key-MD5" location:"headers"`
// Check whether the Etag of copy source matches the specified value
XQSCopySourceIfMatch *string `json:"X-QS-Copy-Source-If-Match,omitempty" name:"X-QS-Copy-Source-If-Match" location:"headers"`
// Check whether the copy source has been modified since the specified date
XQSCopySourceIfModifiedSince *time.Time `json:"X-QS-Copy-Source-If-Modified-Since,omitempty" name:"X-QS-Copy-Source-If-Modified-Since" format:"RFC 822" location:"headers"`
// Check whether the Etag of copy source does not matches the specified value
XQSCopySourceIfNoneMatch *string `json:"X-QS-Copy-Source-If-None-Match,omitempty" name:"X-QS-Copy-Source-If-None-Match" location:"headers"`
// Check whether the copy source has not been unmodified since the specified date
XQSCopySourceIfUnmodifiedSince *time.Time `json:"X-QS-Copy-Source-If-Unmodified-Since,omitempty" name:"X-QS-Copy-Source-If-Unmodified-Since" format:"RFC 822" location:"headers"`
// Encryption algorithm of the object // Encryption algorithm of the object
XQSEncryptionCustomerAlgorithm *string `json:"X-QS-Encryption-Customer-Algorithm,omitempty" name:"X-QS-Encryption-Customer-Algorithm" location:"headers"` XQSEncryptionCustomerAlgorithm *string `json:"X-QS-Encryption-Customer-Algorithm,omitempty" name:"X-QS-Encryption-Customer-Algorithm" location:"headers"`
// Encryption key of the object // Encryption key of the object

68
vendor/github.com/yunify/qingstor-sdk-go/test/image.go generated vendored Normal file
View file

@ -0,0 +1,68 @@
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

@ -41,6 +41,7 @@ func main() {
BucketExternalMirrorFeatureContext(s) BucketExternalMirrorFeatureContext(s)
ObjectFeatureContext(s) ObjectFeatureContext(s)
ObjectMultipartFeatureContext(s) ObjectMultipartFeatureContext(s)
ImageFeatureContext(s)
} }
options := godog.Options{ options := godog.Options{
Format: "pretty", Format: "pretty",

View file

@ -396,6 +396,11 @@ func getObjectWithQuerySignature(objectKey string) error {
errChan <- err errChan <- err
return return
} }
err = r.Build()
if err != nil {
errChan <- err
return
}
err = r.SignQuery(10) err = r.SignQuery(10)
if err != nil { if err != nil {
errChan <- err errChan <- err