Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a75a83be1 | |||
| af66437067 | |||
| e0a739a681 | |||
| d9ff2bfbeb | |||
| fc13b6ebee | |||
| 6355725204 | |||
| b6aa048c4a | |||
| 5da4c33a57 | |||
| 2a316b984d | |||
| fa780870a7 |
@@ -0,0 +1,35 @@
|
||||
name: Go CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go: [1.13, 1.14, 1.15]
|
||||
|
||||
steps:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build Tea
|
||||
run: go build ./tea
|
||||
- name: Build Util
|
||||
run: go build ./utils
|
||||
|
||||
- name: Test
|
||||
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./tea/... ./utils/...
|
||||
|
||||
- name: CodeCov
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
@@ -4,5 +4,7 @@ go 1.14
|
||||
|
||||
require (
|
||||
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/modern-go/reflect2 v1.0.1
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+64
-33
@@ -69,11 +69,12 @@ type Response struct {
|
||||
|
||||
// SDKError struct is used save error code and message
|
||||
type SDKError struct {
|
||||
Code *string
|
||||
Message *string
|
||||
Data *string
|
||||
Stack *string
|
||||
errMsg *string
|
||||
Code *string
|
||||
StatusCode *int
|
||||
Message *string
|
||||
Data *string
|
||||
Stack *string
|
||||
errMsg *string
|
||||
}
|
||||
|
||||
// RuntimeObject is used for converting http configuration
|
||||
@@ -181,9 +182,39 @@ func NewSDKError(obj map[string]interface{}) *SDKError {
|
||||
err.Message = String(obj["message"].(string))
|
||||
}
|
||||
if data := obj["data"]; data != nil {
|
||||
r := reflect.ValueOf(data)
|
||||
if r.Kind().String() == "map" {
|
||||
res := make(map[string]interface{})
|
||||
tmp := r.MapKeys()
|
||||
for _, key := range tmp {
|
||||
res[key.String()] = r.MapIndex(key).Interface()
|
||||
}
|
||||
if statusCode := res["statusCode"]; statusCode != nil {
|
||||
if code, ok := statusCode.(int); ok {
|
||||
err.StatusCode = Int(code)
|
||||
} else if tmp, ok := statusCode.(string); ok {
|
||||
code, err_ := strconv.Atoi(tmp)
|
||||
if err_ == nil {
|
||||
err.StatusCode = Int(code)
|
||||
}
|
||||
} else if code, ok := statusCode.(*int); ok {
|
||||
err.StatusCode = code
|
||||
}
|
||||
}
|
||||
}
|
||||
byt, _ := json.Marshal(data)
|
||||
err.Data = String(string(byt))
|
||||
}
|
||||
|
||||
if statusCode, ok := obj["statusCode"].(int); ok {
|
||||
err.StatusCode = Int(statusCode)
|
||||
} else if status, ok := obj["statusCode"].(string); ok {
|
||||
statusCode, err_ := strconv.Atoi(status)
|
||||
if err_ == nil {
|
||||
err.StatusCode = Int(statusCode)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -194,8 +225,8 @@ func (err *SDKError) SetErrMsg(msg string) {
|
||||
|
||||
func (err *SDKError) Error() string {
|
||||
if err.errMsg == nil {
|
||||
str := fmt.Sprintf("SDKError:\n Code: %s\n Message: %s\n Data: %s\n",
|
||||
StringValue(err.Code), StringValue(err.Message), StringValue(err.Data))
|
||||
str := fmt.Sprintf("SDKError:\n StatusCode: %d\n Code: %s\n Message: %s\n Data: %s\n",
|
||||
IntValue(err.StatusCode), StringValue(err.Code), StringValue(err.Message), StringValue(err.Data))
|
||||
err.SetErrMsg(str)
|
||||
}
|
||||
return StringValue(err.errMsg)
|
||||
@@ -209,7 +240,9 @@ func (err *CastError) Error() string {
|
||||
// Convert is use convert map[string]interface object to struct
|
||||
func Convert(in interface{}, out interface{}) error {
|
||||
byt, _ := json.Marshal(in)
|
||||
err := json.Unmarshal(byt, out)
|
||||
decoder := jsonParser.NewDecoder(bytes.NewReader(byt))
|
||||
decoder.UseNumber()
|
||||
err := decoder.Decode(&out)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -271,20 +304,9 @@ func DoRequest(request *Request, requestRuntime map[string]interface{}) (respons
|
||||
request.Protocol = String(strings.ToLower(StringValue(request.Protocol)))
|
||||
}
|
||||
|
||||
if StringValue(request.Protocol) == "http" {
|
||||
request.Port = Int(80)
|
||||
} else if StringValue(request.Protocol) == "https" {
|
||||
request.Port = Int(443)
|
||||
}
|
||||
|
||||
requestURL := ""
|
||||
request.Domain = request.Headers["host"]
|
||||
matched, _ := regexp.MatchString(":", StringValue(request.Domain))
|
||||
if matched {
|
||||
requestURL = fmt.Sprintf("%s://%s%s", StringValue(request.Protocol), StringValue(request.Domain), StringValue(request.Pathname))
|
||||
} else {
|
||||
requestURL = fmt.Sprintf("%s://%s:%d%s", StringValue(request.Protocol), StringValue(request.Domain), IntValue(request.Port), StringValue(request.Pathname))
|
||||
}
|
||||
requestURL = fmt.Sprintf("%s://%s%s", StringValue(request.Protocol), StringValue(request.Domain), StringValue(request.Pathname))
|
||||
queryParams := request.Query
|
||||
// sort QueryParams by key
|
||||
q := url.Values{}
|
||||
@@ -425,7 +447,7 @@ func getHttpTransport(req *Request, runtime *RuntimeObject) (*http.Transport, er
|
||||
&net.Dialer{
|
||||
Timeout: time.Duration(IntValue(runtime.ConnectTimeout)) * time.Millisecond,
|
||||
DualStack: true,
|
||||
LocalAddr: getLocalAddr(StringValue(runtime.LocalAddr), IntValue(req.Port)),
|
||||
LocalAddr: getLocalAddr(StringValue(runtime.LocalAddr)),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -433,7 +455,7 @@ func getHttpTransport(req *Request, runtime *RuntimeObject) (*http.Transport, er
|
||||
trans.Dial = dialer.Dial
|
||||
}
|
||||
} else {
|
||||
trans.DialContext = setDialContext(runtime, IntValue(req.Port))
|
||||
trans.DialContext = setDialContext(runtime)
|
||||
}
|
||||
return trans, nil
|
||||
}
|
||||
@@ -521,22 +543,20 @@ func getSocks5Proxy(runtime *RuntimeObject) (proxy *url.URL, err error) {
|
||||
return proxy, err
|
||||
}
|
||||
|
||||
func getLocalAddr(localAddr string, port int) (addr *net.TCPAddr) {
|
||||
func getLocalAddr(localAddr string) (addr *net.TCPAddr) {
|
||||
if localAddr != "" {
|
||||
addr = &net.TCPAddr{
|
||||
Port: port,
|
||||
IP: []byte(localAddr),
|
||||
IP: []byte(localAddr),
|
||||
}
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
func setDialContext(runtime *RuntimeObject, port int) func(cxt context.Context, net, addr string) (c net.Conn, err error) {
|
||||
func setDialContext(runtime *RuntimeObject) func(cxt context.Context, net, addr string) (c net.Conn, err error) {
|
||||
return func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if runtime.LocalAddr != nil && StringValue(runtime.LocalAddr) != "" {
|
||||
netAddr := &net.TCPAddr{
|
||||
Port: port,
|
||||
IP: []byte(StringValue(runtime.LocalAddr)),
|
||||
IP: []byte(StringValue(runtime.LocalAddr)),
|
||||
}
|
||||
return (&net.Dialer{
|
||||
Timeout: time.Duration(IntValue(runtime.ConnectTimeout)) * time.Second,
|
||||
@@ -726,7 +746,9 @@ func structToMap(dataValue reflect.Value) map[string]interface{} {
|
||||
if !fieldValue.IsValid() || fieldValue.IsNil() {
|
||||
continue
|
||||
}
|
||||
if field.Type.Kind().String() == "struct" {
|
||||
if field.Type.String() == "io.Reader" || field.Type.String() == "io.Writer" {
|
||||
continue
|
||||
} else if field.Type.Kind().String() == "struct" {
|
||||
out[name] = structToMap(fieldValue)
|
||||
} else if field.Type.Kind().String() == "ptr" &&
|
||||
field.Type.Elem().Kind().String() == "struct" {
|
||||
@@ -776,10 +798,10 @@ func Retryable(err error) *bool {
|
||||
return Bool(false)
|
||||
}
|
||||
if realErr, ok := err.(*SDKError); ok {
|
||||
code, err := strconv.Atoi(StringValue(realErr.Code))
|
||||
if err != nil {
|
||||
return Bool(true)
|
||||
if realErr.StatusCode == nil {
|
||||
return Bool(false)
|
||||
}
|
||||
code := IntValue(realErr.StatusCode)
|
||||
return Bool(code >= http.StatusInternalServerError)
|
||||
}
|
||||
return Bool(true)
|
||||
@@ -959,7 +981,8 @@ func checkRequire(field reflect.StructField, valueField reflect.Value) error {
|
||||
func checkPattern(field reflect.StructField, valueField reflect.Value, tag string) error {
|
||||
if valueField.IsValid() && valueField.String() != "" {
|
||||
value := valueField.String()
|
||||
if match, _ := regexp.MatchString(tag, value); !match { // Determines whether the parameter value satisfies the regular expression or not, and throws an error
|
||||
r, _ := regexp.Compile("^" + tag + "$")
|
||||
if match := r.MatchString(value); !match { // Determines whether the parameter value satisfies the regular expression or not, and throws an error
|
||||
return errors.New(value + " is not matched " + tag)
|
||||
}
|
||||
}
|
||||
@@ -1109,3 +1132,11 @@ func Prettify(i interface{}) string {
|
||||
resp, _ := json.MarshalIndent(i, "", " ")
|
||||
return string(resp)
|
||||
}
|
||||
|
||||
func ToInt(a *int32) *int {
|
||||
return Int(int(Int32Value(a)))
|
||||
}
|
||||
|
||||
func ToInt32(a *int) *int32 {
|
||||
return Int32(int32(IntValue(a)))
|
||||
}
|
||||
|
||||
+108
-16
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -50,14 +51,14 @@ type validateTest struct {
|
||||
Num2 *int `json:"num2,omitempty" maximum:"6"`
|
||||
Name1 *string `json:"name1,omitempty" maxLength:"4"`
|
||||
Name2 *string `json:"name2,omitempty" minLength:"2"`
|
||||
Str *string `json:"str,omitempty" pattern:"^[a-d]*$" maxLength:"4"`
|
||||
Str *string `json:"str,omitempty" pattern:"[a-d]*" maxLength:"4"`
|
||||
MaxLength *errMaxLength `json:"MaxLength,omitempty"`
|
||||
MinLength *errMinLength `json:"MinLength,omitempty"`
|
||||
Maximum *errMaximum `json:"Maximum,omitempty"`
|
||||
Minimum *errMinimum `json:"Minimum,omitempty"`
|
||||
MaxItems *errMaxItems `json:"MaxItems,omitempty"`
|
||||
MinItems *errMinItems `json:"MinItems,omitempty"`
|
||||
List []*string `json:"list,omitempty" pattern:"^[a-d]*$" minItems:"2" maxItems:"3" maxLength:"4"`
|
||||
List []*string `json:"list,omitempty" pattern:"[a-d]*" minItems:"2" maxItems:"3" maxLength:"4"`
|
||||
}
|
||||
|
||||
type errMaxLength struct {
|
||||
@@ -140,12 +141,14 @@ func TestConvert(t *testing.T) {
|
||||
|
||||
func TestConvertType(t *testing.T) {
|
||||
in := map[string]interface{}{
|
||||
"key": 123,
|
||||
"key": 123,
|
||||
"body": []byte("test"),
|
||||
}
|
||||
out := new(test)
|
||||
err := Convert(in, &out)
|
||||
utils.AssertNotNil(t, err)
|
||||
utils.AssertEqual(t, "json: cannot unmarshal number into Go struct field test.key of type string", err.Error())
|
||||
utils.AssertNil(t, err)
|
||||
utils.AssertEqual(t, "123", out.Key)
|
||||
utils.AssertEqual(t, "test", string(out.Body))
|
||||
}
|
||||
|
||||
func TestRuntimeObject(t *testing.T) {
|
||||
@@ -158,8 +161,9 @@ func TestRuntimeObject(t *testing.T) {
|
||||
|
||||
func TestSDKError(t *testing.T) {
|
||||
err := NewSDKError(map[string]interface{}{
|
||||
"code": "code",
|
||||
"message": "message",
|
||||
"code": "code",
|
||||
"statusCode": 404,
|
||||
"message": "message",
|
||||
"data": map[string]interface{}{
|
||||
"httpCode": "404",
|
||||
"requestId": "dfadfa32cgfdcasd4313",
|
||||
@@ -167,19 +171,73 @@ func TestSDKError(t *testing.T) {
|
||||
},
|
||||
})
|
||||
utils.AssertNotNil(t, err)
|
||||
utils.AssertEqual(t, "SDKError:\n Code: code\n Message: message\n Data: {\"hostId\":\"github.com/alibabacloud/tea\",\"httpCode\":\"404\",\"requestId\":\"dfadfa32cgfdcasd4313\"}\n", err.Error())
|
||||
utils.AssertEqual(t, "SDKError:\n StatusCode: 404\n Code: code\n Message: message\n Data: {\"hostId\":\"github.com/alibabacloud/tea\",\"httpCode\":\"404\",\"requestId\":\"dfadfa32cgfdcasd4313\"}\n", err.Error())
|
||||
|
||||
err.SetErrMsg("test")
|
||||
utils.AssertEqual(t, "test", err.Error())
|
||||
utils.AssertEqual(t, 404, *err.StatusCode)
|
||||
|
||||
err = NewSDKError(map[string]interface{}{
|
||||
"statusCode": "404",
|
||||
"data": map[string]interface{}{
|
||||
"statusCode": 500,
|
||||
},
|
||||
})
|
||||
utils.AssertNotNil(t, err)
|
||||
utils.AssertEqual(t, 404, *err.StatusCode)
|
||||
|
||||
err = NewSDKError(map[string]interface{}{
|
||||
"data": map[string]interface{}{
|
||||
"statusCode": 500,
|
||||
},
|
||||
})
|
||||
utils.AssertNotNil(t, err)
|
||||
utils.AssertEqual(t, 500, *err.StatusCode)
|
||||
|
||||
err = NewSDKError(map[string]interface{}{
|
||||
"data": map[string]interface{}{
|
||||
"statusCode": Int(500),
|
||||
},
|
||||
})
|
||||
utils.AssertNotNil(t, err)
|
||||
utils.AssertEqual(t, 500, *err.StatusCode)
|
||||
|
||||
err = NewSDKError(map[string]interface{}{
|
||||
"data": map[string]interface{}{
|
||||
"statusCode": "500",
|
||||
},
|
||||
})
|
||||
utils.AssertNotNil(t, err)
|
||||
utils.AssertEqual(t, 500, *err.StatusCode)
|
||||
|
||||
err = NewSDKError(map[string]interface{}{
|
||||
"code": "code",
|
||||
"message": "message",
|
||||
"data": map[string]interface{}{
|
||||
"requestId": "dfadfa32cgfdcasd4313",
|
||||
},
|
||||
})
|
||||
utils.AssertNotNil(t, err)
|
||||
utils.AssertNil(t, err.StatusCode)
|
||||
|
||||
err = NewSDKError(map[string]interface{}{
|
||||
"code": "code",
|
||||
"message": "message",
|
||||
"data": "string data",
|
||||
})
|
||||
utils.AssertNotNil(t, err)
|
||||
utils.AssertNotNil(t, err.Data)
|
||||
utils.AssertNil(t, err.StatusCode)
|
||||
}
|
||||
|
||||
func TestSDKErrorCode404(t *testing.T) {
|
||||
err := NewSDKError(map[string]interface{}{
|
||||
"code": 404,
|
||||
"message": "message",
|
||||
"statusCode": 404,
|
||||
"code": "NOTFOUND",
|
||||
"message": "message",
|
||||
})
|
||||
utils.AssertNotNil(t, err)
|
||||
utils.AssertEqual(t, "SDKError:\n Code: 404\n Message: message\n Data: \n", err.Error())
|
||||
utils.AssertEqual(t, "SDKError:\n StatusCode: 404\n Code: NOTFOUND\n Message: message\n Data: \n", err.Error())
|
||||
}
|
||||
|
||||
func TestToObject(t *testing.T) {
|
||||
@@ -240,14 +298,18 @@ type Test struct {
|
||||
List []string `json:"List,omitempty"`
|
||||
CastList []CastError `json:"CastList,omitempty"`
|
||||
CastListPtr []*CastError `json:"CastListPtr,omitempty"`
|
||||
Reader io.Reader
|
||||
Inter interface{}
|
||||
}
|
||||
|
||||
func TestToMap(t *testing.T) {
|
||||
in := map[string]*string{
|
||||
"tea": String("test"),
|
||||
"nil": nil,
|
||||
}
|
||||
result := ToMap(in)
|
||||
utils.AssertEqual(t, "test", result["tea"])
|
||||
utils.AssertNil(t, result["nil"])
|
||||
|
||||
validMap := map[string]interface{}{
|
||||
"valid": "test",
|
||||
@@ -273,9 +335,12 @@ func TestToMap(t *testing.T) {
|
||||
Message: String("CastList"),
|
||||
},
|
||||
},
|
||||
Reader: strings.NewReader(""),
|
||||
Inter: 10,
|
||||
}
|
||||
result = ToMap(valid)
|
||||
utils.AssertEqual(t, "tea", result["Msg"])
|
||||
utils.AssertNil(t, result["Reader"])
|
||||
utils.AssertEqual(t, map[string]interface{}{"Message": "message"}, result["Cast"])
|
||||
utils.AssertEqual(t, []interface{}{"test", ""}, result["ListPtr"])
|
||||
utils.AssertEqual(t, []interface{}{"list"}, result["List"])
|
||||
@@ -326,9 +391,29 @@ func Test_Retryable(t *testing.T) {
|
||||
}
|
||||
err = NewSDKError(errmsg)
|
||||
ifRetry = Retryable(err)
|
||||
utils.AssertEqual(t, false, BoolValue(ifRetry))
|
||||
|
||||
errmsg["statusCode"] = 400
|
||||
err = NewSDKError(errmsg)
|
||||
ifRetry = Retryable(err)
|
||||
utils.AssertEqual(t, false, BoolValue(ifRetry))
|
||||
|
||||
errmsg["statusCode"] = "400"
|
||||
err = NewSDKError(errmsg)
|
||||
ifRetry = Retryable(err)
|
||||
utils.AssertEqual(t, false, BoolValue(ifRetry))
|
||||
|
||||
errmsg["statusCode"] = 500
|
||||
err = NewSDKError(errmsg)
|
||||
ifRetry = Retryable(err)
|
||||
utils.AssertEqual(t, true, BoolValue(ifRetry))
|
||||
|
||||
errmsg["code"] = "400"
|
||||
errmsg["statusCode"] = "500"
|
||||
err = NewSDKError(errmsg)
|
||||
ifRetry = Retryable(err)
|
||||
utils.AssertEqual(t, true, BoolValue(ifRetry))
|
||||
|
||||
errmsg["statusCode"] = "test"
|
||||
err = NewSDKError(errmsg)
|
||||
ifRetry = Retryable(err)
|
||||
utils.AssertEqual(t, false, BoolValue(ifRetry))
|
||||
@@ -399,7 +484,6 @@ func Test_DoRequest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
request := NewRequest()
|
||||
request.Port = Int(80)
|
||||
request.Method = String("TEA TEST")
|
||||
resp, err := DoRequest(request, nil)
|
||||
utils.AssertNil(t, resp)
|
||||
@@ -456,7 +540,7 @@ func Test_DoRequest(t *testing.T) {
|
||||
runtimeObj["cert"] = cert
|
||||
runtimeObj["ca"] = "private ca"
|
||||
runtimeObj["socks5Proxy"] = "socks5://someuser:somepassword@cs.aliyun.com"
|
||||
resp, err = DoRequest(request, runtimeObj)
|
||||
_, err = DoRequest(request, runtimeObj)
|
||||
utils.AssertNotNil(t, err)
|
||||
|
||||
runtimeObj["ca"] = ca
|
||||
@@ -558,7 +642,7 @@ func Test_getHttpProxy(t *testing.T) {
|
||||
|
||||
func Test_SetDialContext(t *testing.T) {
|
||||
runtime := &RuntimeObject{}
|
||||
dialcontext := setDialContext(runtime, 80)
|
||||
dialcontext := setDialContext(runtime)
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
utils.AssertNotNil(t, cancelFunc)
|
||||
c, err := dialcontext(ctx, "127.0.0.1", "127.0.0.2")
|
||||
@@ -712,7 +796,7 @@ func Test_validate(t *testing.T) {
|
||||
str2 := "test"
|
||||
val.Str = &str2
|
||||
err = validate(reflect.ValueOf(val))
|
||||
utils.AssertEqual(t, "test is not matched ^[a-d]*$", err.Error())
|
||||
utils.AssertEqual(t, "test is not matched [a-d]*", err.Error())
|
||||
|
||||
val.Str = &str0
|
||||
val.List = []*string{&str0}
|
||||
@@ -780,3 +864,11 @@ func Test_Prettify(t *testing.T) {
|
||||
str = Prettify(nil)
|
||||
utils.AssertEqual(t, str, "null")
|
||||
}
|
||||
|
||||
func Test_TransInt32AndInt(t *testing.T) {
|
||||
a := ToInt(Int32(10))
|
||||
utils.AssertEqual(t, IntValue(a), 10)
|
||||
|
||||
b := ToInt32(a)
|
||||
utils.AssertEqual(t, Int32Value(b), int32(10))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user