Skip to content

Commit

Permalink
支持使用第三方 HTTP Client 或 mock 方法调用结果
Browse files Browse the repository at this point in the history
  • Loading branch information
mozillazg committed Apr 14, 2019
1 parent b71e470 commit 3d6a1c9
Show file tree
Hide file tree
Showing 22 changed files with 432 additions and 56 deletions.
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
language: go
go:
- 1.7
- 1.8
- 1.9
- '1.7.x'
- '1.8.x'
- '1.9.x'
- '1.10.x'
- '1.11.x'
- '1.12.x'
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [0.12.0]

### 新增

* 支持使用使用第三方 http client 包或单元测试时 mock 方法调用结果,示例:[object/mock.go](./_example/object/mock.go)
* 新增 `type Sender interface`
* 新增 `type ResponseParser interface`
* 新增 `type DefaultSender struct`
* 新增 `type DefaultResponseParser struct`


## [0.11.1] (2019-04-14)

Expand Down
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ func main() {
if err != nil {
panic(err)
}
defer resp.Body.Close()
bs, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
fmt.Printf("%s\n", string(bs))
}
```

备注:

* SDK 不会自动设置超时时间,用户根据需要设置合适的超时时间(比如,设置 `http.Client``Timeout` 字段之类的)
或在需要时实现所需的超时机制(比如,通过 `context` 包实现)。
* SDK 不会自动设置超时时间,用户根据需要设置合适的超时时间(比如,设置 `http.Client``Timeout` 字段或者
`Transport` 字段之类的)或在需要时实现所需的超时机制(比如,通过 `context` 包实现)。
* 所有的 API 在 [_example](./_example/) 目录下都有对应的使用示例(示例程序中用到的 `debug` 包只是调试用的不是必需的依赖)。

## TODO
Expand Down Expand Up @@ -112,5 +112,4 @@ Object API:
* [x] 通过预签名授权 URL 下载文件,示例:[object/getWithPresignedURL.go](./_example/object/getWithPresignedURL.go)
* [x] 通过预签名授权 URL 上传文件,示例:[object/putWithPresignedURL.go](./_example/object/putWithPresignedURL.go)
* [ ] 支持临时密钥
* [ ] 支持使用除 net/http 以外的其他 HTTP Client,
方便使用第三方 http 包(比如 fasthttp)或单元测试时 mock 调用结果
* [x] 支持使用使用第三方 http client 包或单元测试时 mock 方法调用结果,示例:[object/mock.go](./_example/object/mock.go)
94 changes: 94 additions & 0 deletions _example/object/mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package main

import (
"bufio"
"bytes"
"context"
"fmt"
"io/ioutil"
"net/http"
"os"
"reflect"

"github.com/mozillazg/go-cos"
"github.com/mozillazg/go-cos/debug"
)

type MockSender struct{}

func (s *MockSender) Send(ctx context.Context, caller cos.Caller, req *http.Request) (*http.Response, error) {
// 如果用不到 response 的话,也可以直接 return &http.Response{}, nil
resp, _ := http.ReadResponse(bufio.NewReader(bytes.NewReader([]byte(`HTTP/1.1 200 OK
Content-Length: 6
Accept-Ranges: bytes
Connection: keep-alive
Content-Type: text/plain; charset=utf-8
Date: Sat, 19 Jan 2019 08:25:27 GMT
Etag: "f572d396fae9206628714fb2ce00f72e94f2258f"
Last-Modified: Mon, 12 Jun 2017 13:36:19 GMT
Server: tencent-cos
X-Cos-Request-Id: NWM0MmRlZjdfMmJhZDM1MGFfNDFkM19hZGI3MQ==
hello
`))), nil)
return resp, nil
}

type MockerResponseParser struct {
result *cos.ObjectGetACLResult
}

func (p *MockerResponseParser) ParseResponse(ctx context.Context, caller cos.Caller, resp *http.Response, result interface{}) (*cos.Response, error) {
b, _ := ioutil.ReadAll(resp.Body)
if string(b) != "hello\n" {
panic(string(b))
}

// 插入预设的结果
switch caller.Method {
case cos.MethodObjectGetACL:
v := result.(*cos.ObjectGetACLResult)
*v = *p.result
}

return &cos.Response{Response: resp}, nil
}

func main() {
b, _ := cos.NewBaseURL("http://cos.example.com")
c := cos.NewClient(b, &http.Client{
Transport: &cos.AuthorizationTransport{
SecretID: os.Getenv("COS_SECRETID"),
SecretKey: os.Getenv("COS_SECRETKEY"),
Transport: &debug.DebugRequestTransport{
RequestHeader: true,
RequestBody: true,
ResponseHeader: true,
ResponseBody: true,
},
},
})
c.Sender = &MockSender{}
acl := &cos.ObjectGetACLResult{
Owner: &cos.Owner{
ID: "test",
},
AccessControlList: []cos.ACLGrant{
{
Permission: "READ",
},
},
}
c.ResponseParser = &MockerResponseParser{acl}

result, resp, err := c.Object.GetACL(context.Background(), "test/mock.go")
if err != nil {
panic(err)
}

defer resp.Body.Close()
fmt.Printf("%#v\n", result)
if !reflect.DeepEqual(*result, *acl) {
panic(*result)
}
}
3 changes: 2 additions & 1 deletion _example/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ run ./object/delete.go
run ./object/deleteMultiple.go
run ./object/copy.go
run ./object/getWithPresignedURL.go
run ./object/putWithPresignedURL.go
run ./object/putWithPresignedURL.go
run ./object/mock.go
1 change: 1 addition & 0 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ type AuthorizationTransport struct {
// 签名多久过期,默认是 time.Hour
Expire time.Duration

// 实际发送 http 请求的 http.RoundTripper,默认使用 http.DefaultTransport
Transport http.RoundTripper
}

Expand Down
12 changes: 6 additions & 6 deletions auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ func TestAuthorizationTransport(t *testing.T) {
}
})

client.client.Transport = &AuthorizationTransport{}
(client.Sender).(*DefaultSender).Transport = &AuthorizationTransport{}
req, _ := http.NewRequest("GET", client.BaseURL.BucketURL.String(), nil)
req.Header.Set("X-Testing", "0")
client.doAPI(context.Background(), req, nil, true)
client.doAPI(context.Background(), Caller{}, req, nil, true)
}

func TestAuthorizationTransport_skip_PresignedURL(t *testing.T) {
Expand All @@ -68,11 +68,11 @@ func TestAuthorizationTransport_skip_PresignedURL(t *testing.T) {
}
})

client.client.Transport = &AuthorizationTransport{}
(client.Sender).(*DefaultSender).Transport = &AuthorizationTransport{}
sign := "q-sign-algorithm=sha1&q-ak=QmFzZTY0IGlzIGEgZ2VuZXJp&q-sign-time=1480932292;1481012292&q-key-time=1480932292;1481012292&q-header-list=&q-url-param-list=&q-signature=a5de76b0734f084a7ea24413f7168b4bdbe5676c"
u := fmt.Sprintf("%s?sign=%s", client.BaseURL.BucketURL.String(), sign)
req, _ := http.NewRequest("GET", u, nil)
client.doAPI(context.Background(), req, nil, true)
client.doAPI(context.Background(), Caller{}, req, nil, true)
}

func TestAuthorizationTransport_with_another_transport(t *testing.T) {
Expand All @@ -87,12 +87,12 @@ func TestAuthorizationTransport_with_another_transport(t *testing.T) {
})

tr := &testingTransport{}
client.client.Transport = &AuthorizationTransport{
(client.Sender).(*DefaultSender).Transport = &AuthorizationTransport{
Transport: tr,
}
req, _ := http.NewRequest("GET", client.BaseURL.BucketURL.String(), nil)
req.Header.Set("X-Testing", "0")
client.doAPI(context.Background(), req, nil, true)
client.doAPI(context.Background(), Caller{}, req, nil, true)
if tr.called != 1 {
t.Error("AuthorizationTransport not call another Transport")
}
Expand Down
24 changes: 24 additions & 0 deletions bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ type BucketGetOptions struct {
MaxKeys int `url:"max-keys,omitempty"`
}

// MethodBucketGet method name of Bucket.Get
const MethodBucketGet MethodName = "Bucket.Get"

// Get Bucket 请求等同于 List Object请求,可以列出该 Bucket 下的部分或者全部 Object。
// 此 API 调用者需要对 Bucket 有 Read 权限。
//
Expand All @@ -67,6 +70,9 @@ func (s *BucketService) Get(ctx context.Context, opt *BucketGetOptions) (*Bucket
method: http.MethodGet,
optQuery: opt,
result: &res,
caller: Caller{
Method: MethodBucketGet,
},
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
Expand All @@ -75,6 +81,9 @@ func (s *BucketService) Get(ctx context.Context, opt *BucketGetOptions) (*Bucket
// BucketPutOptions ...
type BucketPutOptions ACLHeaderOptions

// MethodBucketPut method name of Bucket.Put
const MethodBucketPut MethodName = "Bucket.Put"

// Put Bucket 接口请求可以在指定账号下创建一个 Bucket。该 API 接口不支持匿名请求,
// 您需要使用帯 Authorization 签名认证的请求才能创建新的 Bucket 。
// 创建 Bucket 的用户默认成为 Bucket 的持有者。
Expand All @@ -90,11 +99,17 @@ func (s *BucketService) Put(ctx context.Context, opt *BucketPutOptions) (*Respon
uri: "/",
method: http.MethodPut,
optHeader: opt,
caller: Caller{
Method: MethodBucketPut,
},
}
resp, err := s.client.send(ctx, &sendOpt)
return resp, err
}

// MethodBucketDelete method name of Bucket.Delete
const MethodBucketDelete MethodName = "Bucket.Delete"

// Delete Bucket 请求可以确认该 Bucket 是否存在,是否有权限访问。HEAD 的权限与 Read 一致。
// 当该 Bucket 存在时,返回 HTTP 状态码 200;当该 Bucket 无访问权限时,返回 HTTP 状态码 403;
// 当该 Bucket 不存在时,返回 HTTP 状态码 404。
Expand All @@ -107,11 +122,17 @@ func (s *BucketService) Delete(ctx context.Context) (*Response, error) {
baseURL: s.client.BaseURL.BucketURL,
uri: "/",
method: http.MethodDelete,
caller: Caller{
Method: MethodBucketDelete,
},
}
resp, err := s.client.send(ctx, &sendOpt)
return resp, err
}

// MethodBucketHead method name of Bucket.Head
const MethodBucketHead MethodName = "Bucket.Head"

// Head Bucket请求可以确认是否存在该Bucket,是否有权限访问,Head的权限与Read一致。
//
// 当其存在时,返回 HTTP 状态码200;
Expand All @@ -124,6 +145,9 @@ func (s *BucketService) Head(ctx context.Context) (*Response, error) {
baseURL: s.client.BaseURL.BucketURL,
uri: "/",
method: http.MethodHead,
caller: Caller{
Method: MethodBucketHead,
},
}
resp, err := s.client.send(ctx, &sendOpt)
return resp, err
Expand Down
12 changes: 12 additions & 0 deletions bucket_acl.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (
// https://cloud.tencent.com/document/product/436/7733
type BucketGetACLResult ACLXml

// MethodBucketGetACL method name of Bucket.GetACL
const MethodBucketGetACL MethodName = "Bucket.GetACL"

// GetACL 接口用来获取存储桶的访问权限控制列表。
//
// https://cloud.tencent.com/document/product/436/7733
Expand All @@ -20,6 +23,9 @@ func (s *BucketService) GetACL(ctx context.Context) (*BucketGetACLResult, *Respo
uri: "/?acl",
method: http.MethodGet,
result: &res,
caller: Caller{
Method: MethodBucketGetACL,
},
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
Expand All @@ -32,6 +38,9 @@ type BucketPutACLOptions struct {
Body *ACLXml `url:"-" header:"-"`
}

// MethodBucketPutACL method name of Bucket.PutACL
const MethodBucketPutACL MethodName = "Bucket.PutACL"

// PutACL 使用API写入Bucket的ACL表
//
// Put Bucket ACL 是一个覆盖操作,传入新的ACL将覆盖原有ACL。只有所有者有权操作。
Expand All @@ -52,6 +61,9 @@ func (s *BucketService) PutACL(ctx context.Context, opt *BucketPutACLOptions) (*
method: http.MethodPut,
body: body,
optHeader: header,
caller: Caller{
Method: MethodBucketPutACL,
},
}
resp, err := s.client.send(ctx, &sendOpt)
return resp, err
Expand Down
18 changes: 18 additions & 0 deletions bucket_cors.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ type BucketGetCORSResult struct {
Rules []BucketCORSRule `xml:"CORSRule,omitempty"`
}

// MethodBucketGetCORS method name of Bucket.GetCORS
const MethodBucketGetCORS MethodName = "Bucket.GetCORS"

// GetCORS ...
//
// Get Bucket CORS 接口实现 Bucket 持有者在 Bucket 上进行跨域资源共享的信息配置。
Expand All @@ -47,6 +50,9 @@ func (s *BucketService) GetCORS(ctx context.Context) (*BucketGetCORSResult, *Res
uri: "/?cors",
method: http.MethodGet,
result: &res,
caller: Caller{
Method: MethodBucketGetCORS,
},
}
resp, err := s.client.send(ctx, &sendOpt)
return &res, resp, err
Expand All @@ -61,6 +67,9 @@ type BucketPutCORSOptions struct {
Rules []BucketCORSRule `xml:"CORSRule,omitempty"`
}

// MethodBucketPutCORS method name of Bucket.PutCORS
const MethodBucketPutCORS MethodName = "Bucket.PutCORS"

// PutCORS ...
//
// Put Bucket CORS 接口用来请求设置 Bucket 的跨域资源共享权限,。
Expand All @@ -73,11 +82,17 @@ func (s *BucketService) PutCORS(ctx context.Context, opt *BucketPutCORSOptions)
uri: "/?cors",
method: http.MethodPut,
body: opt,
caller: Caller{
Method: MethodBucketPutCORS,
},
}
resp, err := s.client.send(ctx, &sendOpt)
return resp, err
}

// MethodBucketDeleteCORS method name of Bucket.DeleteCORS
const MethodBucketDeleteCORS MethodName = "Bucket.DeleteCORS"

// DeleteCORS ...
//
// Delete Bucket CORS 接口请求实现删除跨域访问配置信息。
Expand All @@ -88,6 +103,9 @@ func (s *BucketService) DeleteCORS(ctx context.Context) (*Response, error) {
baseURL: s.client.BaseURL.BucketURL,
uri: "/?cors",
method: http.MethodDelete,
caller: Caller{
Method: MethodBucketDeleteCORS,
},
}
resp, err := s.client.send(ctx, &sendOpt)
return resp, err
Expand Down
Loading

0 comments on commit 3d6a1c9

Please sign in to comment.