Skip to content

Commit 3d6a1c9

Browse files
committed
支持使用第三方 HTTP Client 或 mock 方法调用结果
1 parent b71e470 commit 3d6a1c9

22 files changed

+432
-56
lines changed

.travis.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
language: go
22
go:
3-
- 1.7
4-
- 1.8
5-
- 1.9
3+
- '1.7.x'
4+
- '1.8.x'
5+
- '1.9.x'
66
- '1.10.x'
77
- '1.11.x'
88
- '1.12.x'

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## [0.12.0]
4+
5+
### 新增
6+
7+
* 支持使用使用第三方 http client 包或单元测试时 mock 方法调用结果,示例:[object/mock.go](./_example/object/mock.go)
8+
* 新增 `type Sender interface`
9+
* 新增 `type ResponseParser interface`
10+
* 新增 `type DefaultSender struct`
11+
* 新增 `type DefaultResponseParser struct`
12+
313

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

README.md

+4-5
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,16 @@ func main() {
4444
if err != nil {
4545
panic(err)
4646
}
47+
defer resp.Body.Close()
4748
bs, _ := ioutil.ReadAll(resp.Body)
48-
resp.Body.Close()
4949
fmt.Printf("%s\n", string(bs))
5050
}
5151
```
5252

5353
备注:
5454

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

5959
## TODO
@@ -112,5 +112,4 @@ Object API:
112112
* [x] 通过预签名授权 URL 下载文件,示例:[object/getWithPresignedURL.go](./_example/object/getWithPresignedURL.go)
113113
* [x] 通过预签名授权 URL 上传文件,示例:[object/putWithPresignedURL.go](./_example/object/putWithPresignedURL.go)
114114
* [ ] 支持临时密钥
115-
* [ ] 支持使用除 net/http 以外的其他 HTTP Client,
116-
方便使用第三方 http 包(比如 fasthttp)或单元测试时 mock 调用结果
115+
* [x] 支持使用使用第三方 http client 包或单元测试时 mock 方法调用结果,示例:[object/mock.go](./_example/object/mock.go)

_example/object/mock.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"context"
7+
"fmt"
8+
"io/ioutil"
9+
"net/http"
10+
"os"
11+
"reflect"
12+
13+
"github.com/mozillazg/go-cos"
14+
"github.com/mozillazg/go-cos/debug"
15+
)
16+
17+
type MockSender struct{}
18+
19+
func (s *MockSender) Send(ctx context.Context, caller cos.Caller, req *http.Request) (*http.Response, error) {
20+
// 如果用不到 response 的话,也可以直接 return &http.Response{}, nil
21+
resp, _ := http.ReadResponse(bufio.NewReader(bytes.NewReader([]byte(`HTTP/1.1 200 OK
22+
Content-Length: 6
23+
Accept-Ranges: bytes
24+
Connection: keep-alive
25+
Content-Type: text/plain; charset=utf-8
26+
Date: Sat, 19 Jan 2019 08:25:27 GMT
27+
Etag: "f572d396fae9206628714fb2ce00f72e94f2258f"
28+
Last-Modified: Mon, 12 Jun 2017 13:36:19 GMT
29+
Server: tencent-cos
30+
X-Cos-Request-Id: NWM0MmRlZjdfMmJhZDM1MGFfNDFkM19hZGI3MQ==
31+
32+
hello
33+
`))), nil)
34+
return resp, nil
35+
}
36+
37+
type MockerResponseParser struct {
38+
result *cos.ObjectGetACLResult
39+
}
40+
41+
func (p *MockerResponseParser) ParseResponse(ctx context.Context, caller cos.Caller, resp *http.Response, result interface{}) (*cos.Response, error) {
42+
b, _ := ioutil.ReadAll(resp.Body)
43+
if string(b) != "hello\n" {
44+
panic(string(b))
45+
}
46+
47+
// 插入预设的结果
48+
switch caller.Method {
49+
case cos.MethodObjectGetACL:
50+
v := result.(*cos.ObjectGetACLResult)
51+
*v = *p.result
52+
}
53+
54+
return &cos.Response{Response: resp}, nil
55+
}
56+
57+
func main() {
58+
b, _ := cos.NewBaseURL("http://cos.example.com")
59+
c := cos.NewClient(b, &http.Client{
60+
Transport: &cos.AuthorizationTransport{
61+
SecretID: os.Getenv("COS_SECRETID"),
62+
SecretKey: os.Getenv("COS_SECRETKEY"),
63+
Transport: &debug.DebugRequestTransport{
64+
RequestHeader: true,
65+
RequestBody: true,
66+
ResponseHeader: true,
67+
ResponseBody: true,
68+
},
69+
},
70+
})
71+
c.Sender = &MockSender{}
72+
acl := &cos.ObjectGetACLResult{
73+
Owner: &cos.Owner{
74+
ID: "test",
75+
},
76+
AccessControlList: []cos.ACLGrant{
77+
{
78+
Permission: "READ",
79+
},
80+
},
81+
}
82+
c.ResponseParser = &MockerResponseParser{acl}
83+
84+
result, resp, err := c.Object.GetACL(context.Background(), "test/mock.go")
85+
if err != nil {
86+
panic(err)
87+
}
88+
89+
defer resp.Body.Close()
90+
fmt.Printf("%#v\n", result)
91+
if !reflect.DeepEqual(*result, *acl) {
92+
panic(*result)
93+
}
94+
}

_example/test.sh

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,5 @@ run ./object/delete.go
5656
run ./object/deleteMultiple.go
5757
run ./object/copy.go
5858
run ./object/getWithPresignedURL.go
59-
run ./object/putWithPresignedURL.go
59+
run ./object/putWithPresignedURL.go
60+
run ./object/mock.go

auth.go

+1
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ type AuthorizationTransport struct {
272272
// 签名多久过期,默认是 time.Hour
273273
Expire time.Duration
274274

275+
// 实际发送 http 请求的 http.RoundTripper,默认使用 http.DefaultTransport
275276
Transport http.RoundTripper
276277
}
277278

auth_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ func TestAuthorizationTransport(t *testing.T) {
5151
}
5252
})
5353

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

6060
func TestAuthorizationTransport_skip_PresignedURL(t *testing.T) {
@@ -68,11 +68,11 @@ func TestAuthorizationTransport_skip_PresignedURL(t *testing.T) {
6868
}
6969
})
7070

71-
client.client.Transport = &AuthorizationTransport{}
71+
(client.Sender).(*DefaultSender).Transport = &AuthorizationTransport{}
7272
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"
7373
u := fmt.Sprintf("%s?sign=%s", client.BaseURL.BucketURL.String(), sign)
7474
req, _ := http.NewRequest("GET", u, nil)
75-
client.doAPI(context.Background(), req, nil, true)
75+
client.doAPI(context.Background(), Caller{}, req, nil, true)
7676
}
7777

7878
func TestAuthorizationTransport_with_another_transport(t *testing.T) {
@@ -87,12 +87,12 @@ func TestAuthorizationTransport_with_another_transport(t *testing.T) {
8787
})
8888

8989
tr := &testingTransport{}
90-
client.client.Transport = &AuthorizationTransport{
90+
(client.Sender).(*DefaultSender).Transport = &AuthorizationTransport{
9191
Transport: tr,
9292
}
9393
req, _ := http.NewRequest("GET", client.BaseURL.BucketURL.String(), nil)
9494
req.Header.Set("X-Testing", "0")
95-
client.doAPI(context.Background(), req, nil, true)
95+
client.doAPI(context.Background(), Caller{}, req, nil, true)
9696
if tr.called != 1 {
9797
t.Error("AuthorizationTransport not call another Transport")
9898
}

bucket.go

+24
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ type BucketGetOptions struct {
5555
MaxKeys int `url:"max-keys,omitempty"`
5656
}
5757

58+
// MethodBucketGet method name of Bucket.Get
59+
const MethodBucketGet MethodName = "Bucket.Get"
60+
5861
// Get Bucket 请求等同于 List Object请求,可以列出该 Bucket 下的部分或者全部 Object。
5962
// 此 API 调用者需要对 Bucket 有 Read 权限。
6063
//
@@ -67,6 +70,9 @@ func (s *BucketService) Get(ctx context.Context, opt *BucketGetOptions) (*Bucket
6770
method: http.MethodGet,
6871
optQuery: opt,
6972
result: &res,
73+
caller: Caller{
74+
Method: MethodBucketGet,
75+
},
7076
}
7177
resp, err := s.client.send(ctx, &sendOpt)
7278
return &res, resp, err
@@ -75,6 +81,9 @@ func (s *BucketService) Get(ctx context.Context, opt *BucketGetOptions) (*Bucket
7581
// BucketPutOptions ...
7682
type BucketPutOptions ACLHeaderOptions
7783

84+
// MethodBucketPut method name of Bucket.Put
85+
const MethodBucketPut MethodName = "Bucket.Put"
86+
7887
// Put Bucket 接口请求可以在指定账号下创建一个 Bucket。该 API 接口不支持匿名请求,
7988
// 您需要使用帯 Authorization 签名认证的请求才能创建新的 Bucket 。
8089
// 创建 Bucket 的用户默认成为 Bucket 的持有者。
@@ -90,11 +99,17 @@ func (s *BucketService) Put(ctx context.Context, opt *BucketPutOptions) (*Respon
9099
uri: "/",
91100
method: http.MethodPut,
92101
optHeader: opt,
102+
caller: Caller{
103+
Method: MethodBucketPut,
104+
},
93105
}
94106
resp, err := s.client.send(ctx, &sendOpt)
95107
return resp, err
96108
}
97109

110+
// MethodBucketDelete method name of Bucket.Delete
111+
const MethodBucketDelete MethodName = "Bucket.Delete"
112+
98113
// Delete Bucket 请求可以确认该 Bucket 是否存在,是否有权限访问。HEAD 的权限与 Read 一致。
99114
// 当该 Bucket 存在时,返回 HTTP 状态码 200;当该 Bucket 无访问权限时,返回 HTTP 状态码 403;
100115
// 当该 Bucket 不存在时,返回 HTTP 状态码 404。
@@ -107,11 +122,17 @@ func (s *BucketService) Delete(ctx context.Context) (*Response, error) {
107122
baseURL: s.client.BaseURL.BucketURL,
108123
uri: "/",
109124
method: http.MethodDelete,
125+
caller: Caller{
126+
Method: MethodBucketDelete,
127+
},
110128
}
111129
resp, err := s.client.send(ctx, &sendOpt)
112130
return resp, err
113131
}
114132

133+
// MethodBucketHead method name of Bucket.Head
134+
const MethodBucketHead MethodName = "Bucket.Head"
135+
115136
// Head Bucket请求可以确认是否存在该Bucket,是否有权限访问,Head的权限与Read一致。
116137
//
117138
// 当其存在时,返回 HTTP 状态码200;
@@ -124,6 +145,9 @@ func (s *BucketService) Head(ctx context.Context) (*Response, error) {
124145
baseURL: s.client.BaseURL.BucketURL,
125146
uri: "/",
126147
method: http.MethodHead,
148+
caller: Caller{
149+
Method: MethodBucketHead,
150+
},
127151
}
128152
resp, err := s.client.send(ctx, &sendOpt)
129153
return resp, err

bucket_acl.go

+12
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import (
1010
// https://cloud.tencent.com/document/product/436/7733
1111
type BucketGetACLResult ACLXml
1212

13+
// MethodBucketGetACL method name of Bucket.GetACL
14+
const MethodBucketGetACL MethodName = "Bucket.GetACL"
15+
1316
// GetACL 接口用来获取存储桶的访问权限控制列表。
1417
//
1518
// https://cloud.tencent.com/document/product/436/7733
@@ -20,6 +23,9 @@ func (s *BucketService) GetACL(ctx context.Context) (*BucketGetACLResult, *Respo
2023
uri: "/?acl",
2124
method: http.MethodGet,
2225
result: &res,
26+
caller: Caller{
27+
Method: MethodBucketGetACL,
28+
},
2329
}
2430
resp, err := s.client.send(ctx, &sendOpt)
2531
return &res, resp, err
@@ -32,6 +38,9 @@ type BucketPutACLOptions struct {
3238
Body *ACLXml `url:"-" header:"-"`
3339
}
3440

41+
// MethodBucketPutACL method name of Bucket.PutACL
42+
const MethodBucketPutACL MethodName = "Bucket.PutACL"
43+
3544
// PutACL 使用API写入Bucket的ACL表
3645
//
3746
// Put Bucket ACL 是一个覆盖操作,传入新的ACL将覆盖原有ACL。只有所有者有权操作。
@@ -52,6 +61,9 @@ func (s *BucketService) PutACL(ctx context.Context, opt *BucketPutACLOptions) (*
5261
method: http.MethodPut,
5362
body: body,
5463
optHeader: header,
64+
caller: Caller{
65+
Method: MethodBucketPutACL,
66+
},
5567
}
5668
resp, err := s.client.send(ctx, &sendOpt)
5769
return resp, err

bucket_cors.go

+18
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ type BucketGetCORSResult struct {
3333
Rules []BucketCORSRule `xml:"CORSRule,omitempty"`
3434
}
3535

36+
// MethodBucketGetCORS method name of Bucket.GetCORS
37+
const MethodBucketGetCORS MethodName = "Bucket.GetCORS"
38+
3639
// GetCORS ...
3740
//
3841
// Get Bucket CORS 接口实现 Bucket 持有者在 Bucket 上进行跨域资源共享的信息配置。
@@ -47,6 +50,9 @@ func (s *BucketService) GetCORS(ctx context.Context) (*BucketGetCORSResult, *Res
4750
uri: "/?cors",
4851
method: http.MethodGet,
4952
result: &res,
53+
caller: Caller{
54+
Method: MethodBucketGetCORS,
55+
},
5056
}
5157
resp, err := s.client.send(ctx, &sendOpt)
5258
return &res, resp, err
@@ -61,6 +67,9 @@ type BucketPutCORSOptions struct {
6167
Rules []BucketCORSRule `xml:"CORSRule,omitempty"`
6268
}
6369

70+
// MethodBucketPutCORS method name of Bucket.PutCORS
71+
const MethodBucketPutCORS MethodName = "Bucket.PutCORS"
72+
6473
// PutCORS ...
6574
//
6675
// Put Bucket CORS 接口用来请求设置 Bucket 的跨域资源共享权限,。
@@ -73,11 +82,17 @@ func (s *BucketService) PutCORS(ctx context.Context, opt *BucketPutCORSOptions)
7382
uri: "/?cors",
7483
method: http.MethodPut,
7584
body: opt,
85+
caller: Caller{
86+
Method: MethodBucketPutCORS,
87+
},
7688
}
7789
resp, err := s.client.send(ctx, &sendOpt)
7890
return resp, err
7991
}
8092

93+
// MethodBucketDeleteCORS method name of Bucket.DeleteCORS
94+
const MethodBucketDeleteCORS MethodName = "Bucket.DeleteCORS"
95+
8196
// DeleteCORS ...
8297
//
8398
// Delete Bucket CORS 接口请求实现删除跨域访问配置信息。
@@ -88,6 +103,9 @@ func (s *BucketService) DeleteCORS(ctx context.Context) (*Response, error) {
88103
baseURL: s.client.BaseURL.BucketURL,
89104
uri: "/?cors",
90105
method: http.MethodDelete,
106+
caller: Caller{
107+
Method: MethodBucketDeleteCORS,
108+
},
91109
}
92110
resp, err := s.client.send(ctx, &sendOpt)
93111
return resp, err

0 commit comments

Comments
 (0)