| @@ -1,9 +1,13 @@ | |||
| module gitlink.org.cn/cloudream/common | |||
| go 1.20 | |||
| go 1.22 | |||
| toolchain go1.23.2 | |||
| require ( | |||
| github.com/antonfisher/nested-logrus-formatter v1.3.1 | |||
| github.com/aws/aws-sdk-go-v2 v1.36.3 | |||
| github.com/aws/aws-sdk-go-v2/credentials v1.17.62 | |||
| github.com/google/uuid v1.3.0 | |||
| github.com/hashicorp/go-multierror v1.1.1 | |||
| github.com/imdario/mergo v0.3.15 | |||
| @@ -21,14 +25,16 @@ require ( | |||
| golang.org/x/exp v0.0.0-20230519143937-03e91628a987 | |||
| ) | |||
| require github.com/aws/smithy-go v1.22.2 // indirect | |||
| require ( | |||
| github.com/benbjohnson/clock v1.3.0 // indirect | |||
| github.com/coreos/go-semver v0.3.0 // indirect | |||
| github.com/coreos/go-systemd/v22 v22.5.0 // indirect | |||
| github.com/gogo/protobuf v1.3.2 // indirect | |||
| github.com/golang/protobuf v1.5.3 // indirect | |||
| github.com/google/go-querystring v1.1.0 | |||
| github.com/gopherjs/gopherjs v1.17.2 // indirect | |||
| github.com/gorilla/websocket v1.5.3 // indirect | |||
| github.com/hashicorp/errwrap v1.1.0 // indirect | |||
| github.com/jtolds/gls v4.20.0+incompatible // indirect | |||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect | |||
| @@ -1,5 +1,11 @@ | |||
| github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ= | |||
| github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= | |||
| github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= | |||
| github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= | |||
| github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU= | |||
| github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8= | |||
| github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= | |||
| github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= | |||
| github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= | |||
| github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= | |||
| github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= | |||
| @@ -15,15 +21,17 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 | |||
| github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | |||
| github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= | |||
| github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | |||
| github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |||
| github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | |||
| github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | |||
| github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= | |||
| github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= | |||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | |||
| github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | |||
| github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | |||
| github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= | |||
| github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= | |||
| github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= | |||
| github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | |||
| github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | |||
| github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= | |||
| github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= | |||
| @@ -51,6 +59,7 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY | |||
| github.com/otiai10/copy v1.12.0 h1:cLMgSQnXBs1eehF0Wy/FAGsgDTDmAqFR7rQylBb1nDY= | |||
| github.com/otiai10/copy v1.12.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= | |||
| github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= | |||
| github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= | |||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | |||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | |||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | |||
| @@ -75,6 +84,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO | |||
| github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= | |||
| github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | |||
| github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= | |||
| github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= | |||
| github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | |||
| github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | |||
| github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | |||
| @@ -150,6 +160,7 @@ google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw | |||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |||
| gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | |||
| gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | |||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | |||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |||
| @@ -0,0 +1,143 @@ | |||
| package cdsapi | |||
| import ( | |||
| "context" | |||
| "fmt" | |||
| "net/http" | |||
| "net/url" | |||
| "time" | |||
| v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4" | |||
| "github.com/aws/aws-sdk-go-v2/credentials" | |||
| "github.com/google/go-querystring/query" | |||
| cdssdk "gitlink.org.cn/cloudream/common/sdks/storage" | |||
| ) | |||
| const ( | |||
| AuthService = "jcs" | |||
| AuthRegion = "any" | |||
| ) | |||
| type PresignedService struct { | |||
| *Client | |||
| accessKey string | |||
| secretKey string | |||
| } | |||
| func (c *Client) Presigned(accessKey string, secretKey string) *PresignedService { | |||
| return &PresignedService{ | |||
| Client: c, | |||
| accessKey: accessKey, | |||
| secretKey: secretKey, | |||
| } | |||
| } | |||
| const PresignedObjectDownloadByPathPath = "/v1/presigned/object/downloadByPath" | |||
| type PresignedObjectDownloadByPath struct { | |||
| UserID cdssdk.UserID `form:"userID" url:"userID" binding:"required"` | |||
| PackageID cdssdk.PackageID `form:"packageID" url:"packageID" binding:"required"` | |||
| Path string `form:"path" url:"path" binding:"required"` | |||
| Offset int64 `form:"offset" url:"offset,omitempty"` | |||
| Length *int64 `form:"length" url:"length,omitempty"` | |||
| } | |||
| func (c *PresignedService) ObjectDownload(req PresignedObjectDownloadByPath, expireIn int) (string, error) { | |||
| return c.presign(req, PresignedObjectDownloadByPathPath, http.MethodGet, expireIn) | |||
| } | |||
| const PresignedObjectUploadPath = "/v1/presigned/object/upload" | |||
| type PresignedObjectUpload struct { | |||
| UserID cdssdk.UserID `form:"userID" binding:"required" url:"userID"` | |||
| PackageID cdssdk.PackageID `form:"packageID" binding:"required" url:"packageID"` | |||
| Path string `form:"path" binding:"required" url:"path"` | |||
| Affinity cdssdk.StorageID `form:"affinity" url:"affinity,omitempty"` | |||
| LoadTo []cdssdk.StorageID `form:"loadTo" url:"loadTo,omitempty"` | |||
| LoadToPath []string `form:"loadToPath" url:"loadToPath,omitempty"` | |||
| } | |||
| type PresignedObjectUploadResp struct { | |||
| Object cdssdk.Object `json:"object"` | |||
| } | |||
| func (c *PresignedService) ObjectUpload(req PresignedObjectUpload, expireIn int) (string, error) { | |||
| return c.presign(req, PresignedObjectUploadPath, http.MethodPost, expireIn) | |||
| } | |||
| const PresignedObjectNewMultipartUploadPath = "/v1/presigned/object/newMultipartUpload" | |||
| type PresignedObjectNewMultipartUpload struct { | |||
| UserID cdssdk.UserID `form:"userID" binding:"required" url:"userID"` | |||
| PackageID cdssdk.PackageID `form:"packageID" binding:"required" url:"packageID"` | |||
| Path string `form:"path" binding:"required" url:"path"` | |||
| } | |||
| type PresignedObjectNewMultipartUploadResp struct { | |||
| Object cdssdk.Object `json:"object"` | |||
| } | |||
| func (c *PresignedService) ObjectNewMultipartUpload(req PresignedObjectNewMultipartUpload, expireIn int) (string, error) { | |||
| return c.presign(req, PresignedObjectNewMultipartUploadPath, http.MethodPost, expireIn) | |||
| } | |||
| const PresignedObjectUploadPartPath = "/v1/presigned/object/uploadPart" | |||
| type PresignedObjectUploadPart struct { | |||
| UserID cdssdk.UserID `form:"userID" binding:"required" url:"userID"` | |||
| ObjectID cdssdk.ObjectID `form:"objectID" binding:"required" url:"objectID"` | |||
| Index int `form:"index" binding:"required" url:"index"` | |||
| } | |||
| type PresignedUploadPartResp struct{} | |||
| func (c *PresignedService) ObjectUploadPart(req PresignedObjectUploadPart, expireIn int) (string, error) { | |||
| return c.presign(req, PresignedObjectUploadPartPath, http.MethodPost, expireIn) | |||
| } | |||
| const PresignedObjectCompleteMultipartUploadPath = "/v1/presigned/object/completeMultipartUpload" | |||
| type PresignedObjectCompleteMultipartUpload struct { | |||
| UserID cdssdk.UserID `form:"userID" binding:"required" url:"userID"` | |||
| ObjectID cdssdk.ObjectID `form:"objectID" binding:"required" url:"objectID"` | |||
| Indexes []int `form:"indexes" binding:"required" url:"indexes"` | |||
| } | |||
| type PresignedObjectCompleteMultipartUploadResp struct { | |||
| Object cdssdk.Object `json:"object"` | |||
| } | |||
| func (c *PresignedService) ObjectCompleteMultipartUpload(req PresignedObjectCompleteMultipartUpload, expireIn int) (string, error) { | |||
| return c.presign(req, PresignedObjectCompleteMultipartUploadPath, http.MethodPost, expireIn) | |||
| } | |||
| func (c *PresignedService) presign(req any, path string, method string, expireIn int) (string, error) { | |||
| u, err := url.Parse(c.baseURL) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| u = u.JoinPath(path) | |||
| us, err := query.Values(req) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| us.Add("X-Expires", fmt.Sprintf("%v", expireIn)) | |||
| u.RawQuery = us.Encode() | |||
| prod := credentials.NewStaticCredentialsProvider(c.accessKey, c.secretKey, "") | |||
| cred, err := prod.Retrieve(context.TODO()) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| r, err := http.NewRequest(method, u.String(), nil) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| signer := v4.NewSigner() | |||
| signedURL, _, err := signer.PresignHTTP(context.Background(), cred, r, "", AuthService, AuthRegion, time.Now()) | |||
| return signedURL, err | |||
| } | |||
| @@ -0,0 +1,125 @@ | |||
| package cdsapi | |||
| import ( | |||
| "testing" | |||
| . "github.com/smartystreets/goconvey/convey" | |||
| "gitlink.org.cn/cloudream/common/pkgs/types" | |||
| ) | |||
| func Test_Presigned(t *testing.T) { | |||
| cli := NewClient(&Config{ | |||
| URL: "http://localhost:7890", | |||
| }) | |||
| Convey("下载文件", t, func() { | |||
| pre := cli.Presigned("123456", "123456") | |||
| url, err := pre.ObjectDownload(PresignedObjectDownloadByPath{ | |||
| UserID: 1, | |||
| PackageID: 3, | |||
| Path: "example.java", | |||
| Offset: 1, | |||
| Length: types.Ref(int64(100)), | |||
| }, 100) | |||
| So(err, ShouldEqual, nil) | |||
| t.Logf("url: %s", url) | |||
| }) | |||
| Convey("上传文件", t, func() { | |||
| pre := cli.Presigned("123456", "123456") | |||
| url, err := pre.ObjectUpload(PresignedObjectUpload{ | |||
| UserID: 1, | |||
| PackageID: 3, | |||
| Path: "example.java", | |||
| }, 100) | |||
| So(err, ShouldEqual, nil) | |||
| t.Logf("url: %s", url) | |||
| }) | |||
| } | |||
| func Test_PresignedObjectDownload(t *testing.T) { | |||
| cli := NewClient(&Config{ | |||
| URL: "http://localhost:7890", | |||
| }) | |||
| Convey("下载文件", t, func() { | |||
| pre := cli.Presigned("123456", "123456") | |||
| url, err := pre.ObjectDownload(PresignedObjectDownloadByPath{ | |||
| UserID: 1, | |||
| PackageID: 3, | |||
| Path: "example.java", | |||
| // Offset: 1, | |||
| // Length: types.Ref(int64(100)), | |||
| }, 100) | |||
| So(err, ShouldEqual, nil) | |||
| t.Logf("url: %s", url) | |||
| }) | |||
| } | |||
| func Test_PresignedObjectUpload(t *testing.T) { | |||
| cli := NewClient(&Config{ | |||
| URL: "http://localhost:7890", | |||
| }) | |||
| Convey("上传文件", t, func() { | |||
| pre := cli.Presigned("123456", "123456") | |||
| url, err := pre.ObjectUpload(PresignedObjectUpload{ | |||
| UserID: 1, | |||
| PackageID: 3, | |||
| Path: "example.java", | |||
| }, 100) | |||
| So(err, ShouldEqual, nil) | |||
| t.Logf("url: %s", url) | |||
| }) | |||
| } | |||
| func Test_PresignedNewMultipartUpload(t *testing.T) { | |||
| cli := NewClient(&Config{ | |||
| URL: "http://localhost:7890", | |||
| }) | |||
| Convey("启动分片上传", t, func() { | |||
| pre := cli.Presigned("123456", "123456") | |||
| url, err := pre.ObjectNewMultipartUpload(PresignedObjectNewMultipartUpload{ | |||
| UserID: 1, | |||
| PackageID: 3, | |||
| Path: "example.java", | |||
| }, 600) | |||
| So(err, ShouldEqual, nil) | |||
| t.Logf("url: %s", url) | |||
| }) | |||
| } | |||
| func Test_PresignedObjectUploadPart(t *testing.T) { | |||
| cli := NewClient(&Config{ | |||
| URL: "http://localhost:7890", | |||
| }) | |||
| Convey("上传分片", t, func() { | |||
| pre := cli.Presigned("123456", "123456") | |||
| url, err := pre.ObjectUploadPart(PresignedObjectUploadPart{ | |||
| UserID: 1, | |||
| ObjectID: 7, | |||
| Index: 3, | |||
| }, 600) | |||
| So(err, ShouldEqual, nil) | |||
| t.Logf("url: %s", url) | |||
| }) | |||
| } | |||
| func Test_PresignedCompleteMultipartUpload(t *testing.T) { | |||
| cli := NewClient(&Config{ | |||
| URL: "http://localhost:7890", | |||
| }) | |||
| Convey("合并分片", t, func() { | |||
| pre := cli.Presigned("123456", "123456") | |||
| url, err := pre.ObjectCompleteMultipartUpload(PresignedObjectCompleteMultipartUpload{ | |||
| UserID: 1, | |||
| ObjectID: 7, | |||
| Indexes: []int{1, 2, 3}, | |||
| }, 600) | |||
| So(err, ShouldEqual, nil) | |||
| t.Logf("url: %s", url) | |||
| }) | |||
| } | |||
| @@ -6,6 +6,7 @@ import ( | |||
| "fmt" | |||
| "io" | |||
| "reflect" | |||
| "strings" | |||
| jsoniter "github.com/json-iterator/go" | |||
| "github.com/mitchellh/mapstructure" | |||
| @@ -189,3 +190,79 @@ func ObjectToMap(obj any) (map[string]any, error) { | |||
| } | |||
| return mp, dec.Decode(obj) | |||
| } | |||
| // 1. 尝试解开所有引用 | |||
| // | |||
| // 2. nil值将会是空字符串 | |||
| func ObjectToMapString(obj any) (map[string]string, error) { | |||
| if obj == nil { | |||
| return make(map[string]string), nil | |||
| } | |||
| v := reflect.ValueOf(obj) | |||
| for v.Kind() == reflect.Ptr { | |||
| v = v.Elem() | |||
| } | |||
| if !v.IsValid() { | |||
| return make(map[string]string), nil | |||
| } | |||
| if v.Kind() != reflect.Struct { | |||
| return nil, fmt.Errorf("type %v is not a struct", v.Type()) | |||
| } | |||
| mp := make(map[string]string) | |||
| objectToMapString(v, mp) | |||
| return mp, nil | |||
| } | |||
| func objectToMapString(val reflect.Value, mp map[string]string) { | |||
| typ := val.Type() | |||
| for i := 0; i < val.NumField(); i++ { | |||
| vf := val.Field(i) | |||
| tf := typ.Field(i) | |||
| if tf.Anonymous { | |||
| objectToMapString(vf, mp) | |||
| continue | |||
| } | |||
| fieldName := tf.Name | |||
| omitEmpty := false | |||
| jsonTag := tf.Tag.Get("json") | |||
| if jsonTag != "" { | |||
| tagParts := strings.Split(jsonTag, ",") | |||
| fieldName = strings.TrimSpace(tagParts[0]) | |||
| if len(tagParts) > 1 { | |||
| for _, tagPart := range tagParts[1:] { | |||
| tagPart = strings.TrimSpace(tagPart) | |||
| if tagPart == "omitempty" { | |||
| omitEmpty = true | |||
| } | |||
| } | |||
| } | |||
| } | |||
| for vf.Kind() == reflect.Ptr { | |||
| vf = vf.Elem() | |||
| } | |||
| if !vf.IsValid() { | |||
| if omitEmpty { | |||
| continue | |||
| } | |||
| mp[fieldName] = "" | |||
| continue | |||
| } | |||
| if vf.IsZero() && omitEmpty { | |||
| continue | |||
| } | |||
| if vf.Kind() == reflect.Array { | |||
| } | |||
| mp[fieldName] = fmt.Sprintf("%v", vf) | |||
| } | |||
| } | |||
| @@ -694,3 +694,108 @@ func Test_ObjectToJSONEx4(t *testing.T) { | |||
| So(ret[0].(*StCallback).Value, ShouldEqual, "called") | |||
| }) | |||
| } | |||
| type StStringer struct { | |||
| } | |||
| func (s StStringer) String() string { | |||
| return "StStringer" | |||
| } | |||
| func Test_ObjectToMapString(t *testing.T) { | |||
| Convey("结构体", t, func() { | |||
| type StEmb struct { | |||
| IntRef *int `json:"intRef,omitempty"` | |||
| BoolRef **bool `json:"boolRef"` | |||
| StrRef *string `json:"strRef"` | |||
| } | |||
| type St struct { | |||
| StEmb | |||
| Int int | |||
| Bool bool | |||
| Str string | |||
| StStringer *StStringer | |||
| } | |||
| st := St{ | |||
| StEmb: StEmb{ | |||
| IntRef: types.Ref(123), | |||
| BoolRef: types.Ref(types.Ref(true)), | |||
| }, | |||
| Int: 456, | |||
| Bool: false, | |||
| Str: "test", | |||
| StStringer: &StStringer{}, | |||
| } | |||
| mp, err := ObjectToMapString(st) | |||
| So(err, ShouldBeNil) | |||
| So(mp["intRef"], ShouldEqual, "123") | |||
| So(mp["boolRef"], ShouldEqual, "true") | |||
| So(mp["strRef"], ShouldEqual, "") | |||
| So(mp["Int"], ShouldEqual, "456") | |||
| So(mp["Bool"], ShouldEqual, "false") | |||
| So(mp["Str"], ShouldEqual, "test") | |||
| So(mp["StStringer"], ShouldEqual, "StStringer") | |||
| }) | |||
| Convey("结构体引用", t, func() { | |||
| type StEmb struct { | |||
| IntRef *int `json:"intRef,omitempty"` | |||
| BoolRef **bool `json:"boolRef"` | |||
| StrRef *string `json:"strRef"` | |||
| } | |||
| type St struct { | |||
| StEmb | |||
| Int int | |||
| Bool bool | |||
| Str string | |||
| StStringer *StStringer | |||
| } | |||
| st := St{ | |||
| StEmb: StEmb{ | |||
| IntRef: types.Ref(123), | |||
| BoolRef: types.Ref(types.Ref(true)), | |||
| }, | |||
| Int: 456, | |||
| Bool: false, | |||
| Str: "test", | |||
| StStringer: &StStringer{}, | |||
| } | |||
| mp, err := ObjectToMapString(&st) | |||
| So(err, ShouldBeNil) | |||
| So(mp["intRef"], ShouldEqual, "123") | |||
| So(mp["boolRef"], ShouldEqual, "true") | |||
| So(mp["strRef"], ShouldEqual, "") | |||
| So(mp["Int"], ShouldEqual, "456") | |||
| So(mp["Bool"], ShouldEqual, "false") | |||
| So(mp["Str"], ShouldEqual, "test") | |||
| So(mp["StStringer"], ShouldEqual, "StStringer") | |||
| }) | |||
| Convey("nil", t, func() { | |||
| mp, err := ObjectToMapString(nil) | |||
| So(err, ShouldBeNil) | |||
| So(mp, ShouldResemble, map[string]string{}) | |||
| }) | |||
| Convey("nil指针", t, func() { | |||
| type St struct{} | |||
| var st *St | |||
| mp, err := ObjectToMapString(st) | |||
| So(err, ShouldBeNil) | |||
| So(mp, ShouldResemble, map[string]string{}) | |||
| }) | |||
| Convey("非结构体", t, func() { | |||
| mp, err := ObjectToMapString(123) | |||
| So(err, ShouldNotBeNil) | |||
| So(mp, ShouldBeNil) | |||
| }) | |||
| } | |||