Skip to content

Commit

Permalink
feat: api support MessagePack
Browse files Browse the repository at this point in the history
  • Loading branch information
SevereCloud committed Jan 24, 2022
1 parent 1f6b8f5 commit 0ed3f0c
Show file tree
Hide file tree
Showing 25 changed files with 942 additions and 43 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ Version API 5.131.

- [API](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api)
- 500+ methods
- Ability to change the request handler
- Ability to modify HTTP client
- Request Limiter
- Support [MessagePack](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api#VK.EnableMessagePack)
- Token pool
- [OAuth](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/api/oauth)
- [Callback API](https://pkg.go.dev/github.com/SevereCloud/vksdk/v2/callback)
Expand Down
35 changes: 35 additions & 0 deletions api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,41 @@ if errors.As(err, &e) {

Для Execute существует отдельная ошибка `ExecuteErrors`

### Поддержка MessagePack

ВНИМАНИЕ, ЭТО ЭКСПЕРИМЕНТАЛЬНАЯ ФУНКЦИЯ. Некоторые методы могут возвращать
сломанную кодировку.

VK API способно возвращать ответ в виде [MessagePack](https://msgpack.org/).
Это эффективный формат двоичной сериализации, похожий на JSON, только быстрее
и меньше по размеру.

```go
vk := api.NewVK(os.Getenv("USER_TOKEN"))

method := "store.getStickersKeywords"
params := api.Params{
"aliases": true,
"all_products": true,
"need_stickers": true,
}

r, err := vk.Request(method, params)
if err != nil {
log.Fatal(err)
}
log.Println("json:", len(r)) // json: 814231

// Включаем поддержку MessagePack
vk.EnableMessagePack(true)

r, err = vk.Request(method, params)
if err != nil {
log.Fatal(err)
}
log.Println("msgpack:", len(r)) // msgpack: 650775
```

### Запрос любого метода

Пример запроса [users.get](https://vk.com/dev/users.get)
Expand Down
19 changes: 19 additions & 0 deletions api/ads.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"

import (
"bytes"
"encoding/json"

"github.com/SevereCloud/vksdk/v2/object"
"github.com/vmihailenco/msgpack/v5"
)

// AdsAddOfficeUsersItem struct.
Expand All @@ -21,6 +23,23 @@ func (r *AdsAddOfficeUsersItem) UnmarshalJSON(data []byte) (err error) {
return
}

// DecodeMsgpack func.
func (r *AdsAddOfficeUsersItem) DecodeMsgpack(dec *msgpack.Decoder) error {
data, err := dec.DecodeRaw()
if err != nil {
return err
}

if msgpack.Unmarshal(data, &r.OK) != nil {
d := msgpack.NewDecoder(bytes.NewReader(data))
d.SetCustomStructTag("json")

return d.Decode(&r.Error)
}

return nil
}

// AdsAddOfficeUsersResponse struct.
type AdsAddOfficeUsersResponse []AdsAddOfficeUsersItem

Expand Down
42 changes: 42 additions & 0 deletions api/ads_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (

"github.com/SevereCloud/vksdk/v2/api"
"github.com/stretchr/testify/assert"
"github.com/vmihailenco/msgpack/v5"
"github.com/vmihailenco/msgpack/v5/msgpcode"
)

func TestAdsResponse_UnmarshalJSON(t *testing.T) {
Expand Down Expand Up @@ -32,6 +34,46 @@ func TestAdsResponse_UnmarshalJSON(t *testing.T) {
)
}

func TestAdsResponse_DecodeMsgpack(t *testing.T) {
t.Parallel()

f := func(data []byte, expected api.AdsAddOfficeUsersItem, wantErr string) {
var r api.AdsAddOfficeUsersItem

err := msgpack.Unmarshal(data, &r)
if err != nil || wantErr != "" {
assert.EqualError(t, err, wantErr)
}

assert.Equal(t, expected, r)
}

f([]byte{msgpcode.False}, api.AdsAddOfficeUsersItem{OK: false}, "")
f([]byte{msgpcode.True}, api.AdsAddOfficeUsersItem{OK: true}, "")
f(
[]byte{
0x82, 0xAA, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x63, 0x6F, 0x64,
0x65, 0x64, 0xAA, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x5F, 0x64, 0x65,
0x73, 0x63, 0xD9, 0x48, 0x4F, 0x6E, 0x65, 0x20, 0x6F, 0x66, 0x20,
0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x65, 0x74,
0x65, 0x72, 0x73, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69,
0x65, 0x64, 0x20, 0x77, 0x61, 0x73, 0x20, 0x6D, 0x69, 0x73, 0x73,
0x69, 0x6E, 0x67, 0x20, 0x6F, 0x72, 0x20, 0x69, 0x6E, 0x76, 0x61,
0x6C, 0x69, 0x64, 0x3A, 0x20, 0x64, 0x61, 0x74, 0x61, 0x5B, 0x31,
0x5D, 0x5B, 0x75, 0x73, 0x65, 0x72, 0x5F, 0x69, 0x64, 0x5D,
},
api.AdsAddOfficeUsersItem{
OK: false,
Error: api.AdsError{
Code: 100,
Desc: "One of the parameters specified was missing or invalid: data[1][user_id]",
},
},
"",
)
f(nil, api.AdsAddOfficeUsersItem{}, "EOF")
}

func TestVK_AdsGetAccounts(t *testing.T) {
t.Parallel()

Expand Down
57 changes: 46 additions & 11 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/SevereCloud/vksdk/v2"
"github.com/SevereCloud/vksdk/v2/internal"
"github.com/SevereCloud/vksdk/v2/object"
"github.com/vmihailenco/msgpack/v5"
)

// Api constants.
Expand Down Expand Up @@ -91,16 +92,18 @@ type VK struct {
UserAgent string
Handler func(method string, params ...Params) (Response, error)

msgpack bool

mux sync.Mutex
lastTime time.Time
rps int
}

// Response struct.
type Response struct {
Response json.RawMessage `json:"response"`
Error Error `json:"error"`
ExecuteErrors ExecuteErrors `json:"execute_errors"`
Response object.RawMessage `json:"response"`
Error Error `json:"error"`
ExecuteErrors ExecuteErrors `json:"execute_errors"`
}

// NewVK returns a new VK.
Expand Down Expand Up @@ -252,17 +255,27 @@ func (vk *VK) DefaultHandler(method string, sliceParams ...Params) (Response, er
}

mediatype, _, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
if mediatype != "application/json" {
switch mediatype {
case "application/json":
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
_ = resp.Body.Close()
return response, err
}
case "application/x-msgpack":
dec := msgpack.NewDecoder(resp.Body)
dec.SetCustomStructTag("json")

err = dec.Decode(&response)
if err != nil {
_ = resp.Body.Close()
return response, err
}
default:
_ = resp.Body.Close()
return response, &InvalidContentType{mediatype}
}

err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
_ = resp.Body.Close()
return response, err
}

_ = resp.Body.Close()

switch response.Error.Code {
Expand Down Expand Up @@ -291,6 +304,10 @@ func (vk *VK) Request(method string, sliceParams ...Params) ([]byte, error) {

sliceParams = append(sliceParams, reqParams)

if vk.msgpack {
method += ".msgpack"
}

resp, err := vk.Handler(method, sliceParams...)

return resp.Response, err
Expand All @@ -303,7 +320,25 @@ func (vk *VK) RequestUnmarshal(method string, obj interface{}, sliceParams ...Pa
return err
}

return json.Unmarshal(rawResponse, &obj)
if vk.msgpack {
dec := msgpack.NewDecoder(bytes.NewReader(rawResponse))
dec.SetCustomStructTag("json")

err = dec.Decode(&obj)
} else {
err = json.Unmarshal(rawResponse, &obj)
}

return err
}

// EnableMessagePack enable using MessagePack instead of JSON.
//
// THIS IS EXPERIMENTAL FUNCTION! Broken encoding returned in some methods.
//
// See https://msgpack.org
func (vk *VK) EnableMessagePack() {
vk.msgpack = true
}

func fmtReflectValue(value reflect.Value, depth int) string {
Expand Down
23 changes: 19 additions & 4 deletions api/execute.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package api

import "encoding/json"
import (
"bytes"
"encoding/json"

"github.com/vmihailenco/msgpack/v5"
)

// ExecuteWithArgs a universal method for calling a sequence of other methods
// while saving and filtering interim results.
Expand All @@ -26,9 +31,19 @@ func (vk *VK) ExecuteWithArgs(code string, params Params, obj interface{}) error
return err
}

jsonErr := json.Unmarshal(resp.Response, &obj)
if jsonErr != nil {
return jsonErr
var decoderErr error

if vk.msgpack {
dec := msgpack.NewDecoder(bytes.NewReader(resp.Response))
dec.SetCustomStructTag("json")

decoderErr = dec.Decode(&obj)
} else {
decoderErr = json.Unmarshal(resp.Response, &obj)
}

if decoderErr != nil {
return decoderErr
}

if resp.ExecuteErrors != nil {
Expand Down
26 changes: 26 additions & 0 deletions api/messages.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package api // import "github.com/SevereCloud/vksdk/v2/api"

import (
"strconv"

"github.com/SevereCloud/vksdk/v2/object"
"github.com/vmihailenco/msgpack/v5"
)

// MessagesAddChatUser adds a new user to a chat.
Expand Down Expand Up @@ -31,11 +34,34 @@ func (vk *VK) MessagesCreateChat(params Params) (response int, err error) {
// MessagesDeleteResponse struct.
type MessagesDeleteResponse map[string]int

// DecodeMsgpack funcion.
func (resp *MessagesDeleteResponse) DecodeMsgpack(dec *msgpack.Decoder) error {
data, err := dec.DecodeRaw()
if err != nil {
return err
}

var respMap map[int]int

err = msgpack.Unmarshal(data, &respMap)
if err != nil {
return err
}

*resp = make(MessagesDeleteResponse)
for key, val := range respMap {
(*resp)[strconv.Itoa(key)] = val
}

return nil
}

// MessagesDelete deletes one or more messages.
//
// https://vk.com/dev/messages.delete
func (vk *VK) MessagesDelete(params Params) (response MessagesDeleteResponse, err error) {
err = vk.RequestUnmarshal("messages.delete", &response, params)

return
}

Expand Down
4 changes: 2 additions & 2 deletions api/oauth/group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestNewGroupTokensFromJSON(t *testing.T) {
t.Helper()

token, err := oauth.NewGroupTokensFromJSON(data)
if err != nil {
if err != nil || wantErr != "" {
assert.EqualError(t, err, wantErr)
}

Expand Down Expand Up @@ -53,7 +53,7 @@ func TestNewGroupTokensFromURL(t *testing.T) {
u, _ := url.Parse(rawurl)

token, err := oauth.NewGroupTokensFromURL(u)
if err != nil {
if err != nil || wantErr != "" {
assert.EqualError(t, err, wantErr)
}

Expand Down
4 changes: 2 additions & 2 deletions api/oauth/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func TestParseJSON(t *testing.T) {

f := func(data []byte, wantToken *oauth.UserToken, wantErr string) {
token, err := oauth.NewUserTokenFromJSON(data)
if err != nil {
if err != nil || wantErr != "" {
assert.EqualError(t, err, wantErr)
}

Expand Down Expand Up @@ -47,7 +47,7 @@ func TestParseURL(t *testing.T) {
u, _ := url.Parse(rawurl)

token, err := oauth.NewUserTokenFromURL(u)
if err != nil {
if err != nil || wantErr != "" {
assert.EqualError(t, err, wantErr)
}

Expand Down
5 changes: 2 additions & 3 deletions api/streaming_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"testing"

"github.com/SevereCloud/vksdk/v2/api"
"github.com/stretchr/testify/assert"
)

func TestVK_StreamingGetServerURL(t *testing.T) {
Expand Down Expand Up @@ -72,9 +73,7 @@ func TestVK_StreamingGetStem(t *testing.T) {
t.Errorf("%v", err)
}

if response.Stem != "собак" {
t.Error("Stem wrong")
}
assert.Equal(t, "собак", response.Stem)
}

func TestVK_StreamingSetSettings(t *testing.T) {
Expand Down
Loading

0 comments on commit 0ed3f0c

Please sign in to comment.