feat: incr sync version.
This commit is contained in:
51
go/chao-sdk-core/msgtest/module/api_msg_sender.go
Normal file
51
go/chao-sdk-core/msgtest/module/api_msg_sender.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package module
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
|
||||
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
)
|
||||
|
||||
type ApiMsgSender struct {
|
||||
*MetaManager
|
||||
}
|
||||
|
||||
type SendMsgReq struct {
|
||||
RecvID string `json:"recvID" binding:"required_if" message:"recvID is required if sessionType is SingleChatType or NotificationChatType"`
|
||||
SendMsg
|
||||
}
|
||||
|
||||
type SendMsg struct {
|
||||
SendID string `json:"sendID" binding:"required"`
|
||||
GroupID string `json:"groupID" binding:"required_if=SessionType 2|required_if=SessionType 3"`
|
||||
SenderNickname string `json:"senderNickname"`
|
||||
SenderFaceURL string `json:"senderFaceURL"`
|
||||
SenderPlatformID int32 `json:"senderPlatformID"`
|
||||
Content map[string]interface{} `json:"content" binding:"required" swaggerignore:"true"`
|
||||
ContentType int32 `json:"contentType" binding:"required"`
|
||||
SessionType int32 `json:"sessionType" binding:"required"`
|
||||
IsOnlineOnly bool `json:"isOnlineOnly"`
|
||||
NotOfflinePush bool `json:"notOfflinePush"`
|
||||
OfflinePushInfo *sdkws.OfflinePushInfo `json:"offlinePushInfo"`
|
||||
}
|
||||
|
||||
func (a *ApiMsgSender) SendMsg(sendID, recvID string, index int) error {
|
||||
content := fmt.Sprintf("this is test msg user %s to user %s, index: %d", sendID, recvID, index)
|
||||
text := sdk_struct.TextElem{Content: content}
|
||||
req := &SendMsgReq{
|
||||
RecvID: recvID,
|
||||
SendMsg: SendMsg{
|
||||
SendID: sendID,
|
||||
SenderPlatformID: constant.WindowsPlatformID,
|
||||
ContentType: constant.Text,
|
||||
SessionType: constant.SingleChatType,
|
||||
Content: map[string]interface{}{"content": utils.StructToJsonString(text)},
|
||||
},
|
||||
}
|
||||
var resp msg.SendMsgResp
|
||||
return a.postWithCtx(constant.SendMsgRouter, req, &resp)
|
||||
}
|
||||
18
go/chao-sdk-core/msgtest/module/friend_manager.go
Normal file
18
go/chao-sdk-core/msgtest/module/friend_manager.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package module
|
||||
|
||||
import (
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
|
||||
"github.com/openimsdk/protocol/relation"
|
||||
)
|
||||
|
||||
type TestFriendManager struct {
|
||||
*MetaManager
|
||||
}
|
||||
|
||||
func (t *TestFriendManager) ImportFriends(ownerUserID string, friendUserIDs []string) error {
|
||||
req := &relation.ImportFriendReq{
|
||||
OwnerUserID: ownerUserID,
|
||||
FriendUserIDs: friendUserIDs,
|
||||
}
|
||||
return t.postWithCtx(constant.ImportFriendListRouter, &req, nil)
|
||||
}
|
||||
75
go/chao-sdk-core/msgtest/module/group_manager.go
Normal file
75
go/chao-sdk-core/msgtest/module/group_manager.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package module
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/protocol/group"
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
)
|
||||
|
||||
type TestGroupManager struct {
|
||||
*MetaManager
|
||||
}
|
||||
|
||||
func (t *TestGroupManager) GenGroupID(prefix string) string {
|
||||
return fmt.Sprintf("%s_test_group_id_%d", prefix, time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func (t *TestGroupManager) CreateGroup(groupID string, groupName string, ownerUserID string, userIDs []string) error {
|
||||
const batch = 2000
|
||||
var memberUserIDs []string
|
||||
if len(userIDs) > batch {
|
||||
memberUserIDs = userIDs[:batch]
|
||||
} else {
|
||||
memberUserIDs = userIDs
|
||||
}
|
||||
req := &group.CreateGroupReq{
|
||||
MemberUserIDs: memberUserIDs,
|
||||
OwnerUserID: ownerUserID,
|
||||
GroupInfo: &sdkws.GroupInfo{
|
||||
GroupID: groupID,
|
||||
GroupName: groupName,
|
||||
GroupType: constant.WorkingGroup,
|
||||
CreatorUserID: ownerUserID,
|
||||
},
|
||||
}
|
||||
resp := &group.CreateGroupResp{}
|
||||
if err := t.postWithCtx(constant.CreateGroupRouter, &req, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(userIDs) > batch {
|
||||
num := len(userIDs) / batch
|
||||
if len(userIDs)%batch != 0 {
|
||||
num++
|
||||
}
|
||||
for i := 1; i < num; i++ {
|
||||
start := batch * i
|
||||
end := batch*i + batch
|
||||
if len(userIDs) < end {
|
||||
end = len(userIDs)
|
||||
}
|
||||
req := map[string]any{
|
||||
"groupID": groupID,
|
||||
"invitedUserIDs": userIDs[start:end],
|
||||
"reason": "test",
|
||||
}
|
||||
resp := struct{}{}
|
||||
if err := t.postWithCtx(constant.RouterGroup+"/invite_user_to_group", req, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *TestGroupManager) InviteUserToGroup(ctx context.Context, groupID string, invitedUserIDs []string) error {
|
||||
req := &group.InviteUserToGroupReq{
|
||||
GroupID: groupID,
|
||||
InvitedUserIDs: invitedUserIDs,
|
||||
}
|
||||
resp := &group.InviteUserToGroupResp{}
|
||||
return t.postWithCtx(constant.InviteUserToGroupRouter, &req, &resp)
|
||||
}
|
||||
140
go/chao-sdk-core/msgtest/module/manager.go
Normal file
140
go/chao-sdk-core/msgtest/module/manager.go
Normal file
@@ -0,0 +1,140 @@
|
||||
package module
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/internal/util"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/sdkerrs"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
|
||||
authPB "github.com/openimsdk/protocol/auth"
|
||||
"github.com/openimsdk/protocol/msg"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
ManagerUserID = "openIMAdmin"
|
||||
)
|
||||
|
||||
type MetaManager struct {
|
||||
managerUserID string
|
||||
apiAddr string
|
||||
secret string
|
||||
token string
|
||||
}
|
||||
|
||||
func NewMetaManager(apiAddr, secret, managerUserID string) *MetaManager {
|
||||
return &MetaManager{
|
||||
managerUserID: managerUserID,
|
||||
apiAddr: apiAddr,
|
||||
secret: secret,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MetaManager) NewUserManager() *TestUserManager {
|
||||
return &TestUserManager{m}
|
||||
}
|
||||
|
||||
func (m *MetaManager) NewGroupMananger() *TestGroupManager {
|
||||
return &TestGroupManager{m}
|
||||
}
|
||||
|
||||
func (m *MetaManager) NewFriendManager() *TestFriendManager {
|
||||
return &TestFriendManager{m}
|
||||
}
|
||||
|
||||
func (m *MetaManager) NewApiMsgSender() *ApiMsgSender {
|
||||
return &ApiMsgSender{m}
|
||||
}
|
||||
|
||||
func (m *MetaManager) apiPost(ctx context.Context, route string, req, resp any) (err error) {
|
||||
operationID, _ := ctx.Value("operationID").(string)
|
||||
if operationID == "" {
|
||||
err := sdkerrs.ErrArgs.Wrap("call api operationID is empty")
|
||||
return err
|
||||
}
|
||||
reqBody, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return sdkerrs.ErrSdkInternal.Wrap("json.Marshal(req) failed " + err.Error())
|
||||
}
|
||||
reqUrl := m.apiAddr + route
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPost, reqUrl, bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
return sdkerrs.ErrSdkInternal.Wrap("sdk http.NewRequestWithContext failed " + err.Error())
|
||||
}
|
||||
log.ZDebug(ctx, "ApiRequest", "url", reqUrl, "body", string(reqBody))
|
||||
request.ContentLength = int64(len(reqBody))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
request.Header.Set("operationID", operationID)
|
||||
if m.token != "" {
|
||||
request.Header.Set("token", m.token)
|
||||
}
|
||||
response, err := new(http.Client).Do(request)
|
||||
if err != nil {
|
||||
return sdkerrs.ErrNetwork.Wrap("ApiPost http.Client.Do failed " + err.Error())
|
||||
}
|
||||
defer response.Body.Close()
|
||||
respBody, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "ApiResponse", err, "type", "read body", "status", response.Status)
|
||||
return sdkerrs.ErrSdkInternal.Wrap("io.ReadAll(ApiResponse) failed " + err.Error())
|
||||
}
|
||||
log.ZDebug(ctx, "ApiResponse", "url", reqUrl, "status", response.Status, "body", string(respBody))
|
||||
var baseApi util.ApiResponse
|
||||
if err := json.Unmarshal(respBody, &baseApi); err != nil {
|
||||
return sdkerrs.ErrSdkInternal.Wrap(fmt.Sprintf("api %s json.Unmarshal(%q, %T) failed %s", m.apiAddr, string(respBody), &baseApi, err.Error()))
|
||||
}
|
||||
if baseApi.ErrCode != 0 {
|
||||
err := sdkerrs.New(baseApi.ErrCode, baseApi.ErrMsg, baseApi.ErrDlt)
|
||||
return err
|
||||
}
|
||||
if resp == nil || len(baseApi.Data) == 0 || string(baseApi.Data) == "null" {
|
||||
return nil
|
||||
}
|
||||
if err := json.Unmarshal(baseApi.Data, resp); err != nil {
|
||||
return sdkerrs.ErrSdkInternal.Wrap(fmt.Sprintf("json.Unmarshal(%q, %T) failed %s", string(baseApi.Data), resp, err.Error()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MetaManager) postWithCtx(route string, req, resp any) error {
|
||||
return m.apiPost(m.buildCtx(), route, req, resp)
|
||||
}
|
||||
|
||||
func (m *MetaManager) buildCtx() context.Context {
|
||||
return mcontext.NewCtx(utils.OperationIDGenerator())
|
||||
}
|
||||
|
||||
func (m *MetaManager) getToken(userID string, platformID int32) (string, error) {
|
||||
req := authPB.UserTokenReq{PlatformID: platformID, UserID: userID, Secret: m.secret}
|
||||
resp := authPB.UserTokenResp{}
|
||||
err := m.postWithCtx(constant.GetUsersToken, &req, &resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.Token, nil
|
||||
}
|
||||
|
||||
func (m *MetaManager) initToken() error {
|
||||
token, err := m.getToken(m.managerUserID, constant.AdminPlatformID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.token = token
|
||||
return nil
|
||||
}
|
||||
func (m *MetaManager) GetServerTime() (int64, error) {
|
||||
req := msg.GetServerTimeReq{}
|
||||
resp := msg.GetServerTimeResp{}
|
||||
err := m.postWithCtx(constant.GetServerTimeRouter, &req, &resp)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return resp.ServerTime, nil
|
||||
}
|
||||
}
|
||||
183
go/chao-sdk-core/msgtest/module/msg_sender.go
Normal file
183
go/chao-sdk-core/msgtest/module/msg_sender.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package module
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/internal/interaction"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/ccontext"
|
||||
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/common"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/utils"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/sdk_struct"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
"github.com/openimsdk/tools/log"
|
||||
"github.com/openimsdk/tools/mcontext"
|
||||
)
|
||||
|
||||
type SendMsgUser struct {
|
||||
longConnMgr *interaction.LongConnMgr
|
||||
userID string
|
||||
pushMsgAndMaxSeqCh chan common.Cmd2Value
|
||||
recvPushMsgCallback func(msg *sdkws.MsgData)
|
||||
failedMessageMap map[string]error
|
||||
cancelFunc context.CancelFunc
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (b SendMsgUser) GetUserID() string {
|
||||
return b.userID
|
||||
}
|
||||
|
||||
func WithRecvPushMsgCallback(callback func(msg *sdkws.MsgData)) func(core *SendMsgUser) {
|
||||
return func(core *SendMsgUser) {
|
||||
core.recvPushMsgCallback = callback
|
||||
}
|
||||
}
|
||||
|
||||
func newIMconfig(platformID int32, apiAddr, wsAddr string) sdk_struct.IMConfig {
|
||||
return sdk_struct.IMConfig{
|
||||
PlatformID: platformID,
|
||||
ApiAddr: apiAddr,
|
||||
WsAddr: wsAddr,
|
||||
}
|
||||
}
|
||||
|
||||
func newUserCtx(userID, token string, imConfig sdk_struct.IMConfig) context.Context {
|
||||
return ccontext.WithInfo(context.Background(), &ccontext.GlobalConfig{
|
||||
UserID: userID,
|
||||
Token: token,
|
||||
IMConfig: imConfig})
|
||||
}
|
||||
|
||||
func NewUser(userID, token string, imConfig sdk_struct.IMConfig, opts ...func(core *SendMsgUser)) *SendMsgUser {
|
||||
pushMsgAndMaxSeqCh := make(chan common.Cmd2Value, 1000)
|
||||
ctx := newUserCtx(userID, token, imConfig)
|
||||
longConnMgr := interaction.NewLongConnMgr(ctx, &ConnListner{}, nil, pushMsgAndMaxSeqCh, nil)
|
||||
core := &SendMsgUser{
|
||||
pushMsgAndMaxSeqCh: pushMsgAndMaxSeqCh,
|
||||
longConnMgr: longConnMgr,
|
||||
userID: userID,
|
||||
failedMessageMap: make(map[string]error),
|
||||
ctx: ctx,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(core)
|
||||
}
|
||||
baseCtx, cancel := context.WithCancel(ctx)
|
||||
core.cancelFunc = cancel
|
||||
go core.recvPushMsg(baseCtx)
|
||||
go core.longConnMgr.Run(baseCtx)
|
||||
return core
|
||||
}
|
||||
|
||||
func (b *SendMsgUser) Close(ctx context.Context) {
|
||||
b.longConnMgr.Close(ctx)
|
||||
b.cancelFunc()
|
||||
}
|
||||
|
||||
func (b *SendMsgUser) SendMsgWithContext(userID string, index int) error {
|
||||
newCtx := mcontext.SetOperationID(b.ctx, utils.OperationIDGenerator())
|
||||
return b.SendSingleMsg(newCtx, userID, index)
|
||||
}
|
||||
|
||||
func (b *SendMsgUser) SendGroupMsgWithContext(groupID string, index int) error {
|
||||
newCtx := mcontext.SetOperationID(b.ctx, utils.OperationIDGenerator())
|
||||
return b.SendGroupMsg(newCtx, groupID, index)
|
||||
|
||||
}
|
||||
|
||||
func (b *SendMsgUser) SendSingleMsg(ctx context.Context, userID string, index int) error {
|
||||
return b.sendMsg(ctx, userID, "", index, constant.SingleChatType, fmt.Sprintf("this is test msg user %s to user %s, index: %d", b.userID, userID, index))
|
||||
}
|
||||
|
||||
func (b *SendMsgUser) BatchSendSingleMsg(ctx context.Context, userID string, index int) error {
|
||||
content := fmt.Sprintf("this is test msg user %s to user %s, index: %d", b.userID, userID, index)
|
||||
err := b.sendMsg(ctx, userID, "", index, constant.SingleChatType, content)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "send msg failed", err, "userID", userID, "index", index, "content", content)
|
||||
b.failedMessageMap[content] = err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SendMsgUser) SendGroupMsg(ctx context.Context, groupID string, index int) error {
|
||||
return b.sendMsg(ctx, "", groupID, index, constant.SuperGroupChatType, fmt.Sprintf("this is test msg user %s to group %s, index: %d", b.userID, groupID, index))
|
||||
}
|
||||
|
||||
func (b *SendMsgUser) BatchSendGroupMsg(ctx context.Context, groupID string, index int) error {
|
||||
content := fmt.Sprintf("this is test msg user %s to group %s, index: %d", b.userID, groupID, index)
|
||||
err := b.sendMsg(ctx, "", groupID, index, constant.SuperGroupChatType, content)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "send msg failed", err, "groupID", groupID, "index", index, "content", content)
|
||||
b.failedMessageMap[content] = err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SendMsgUser) sendMsg(ctx context.Context, userID, groupID string, index int, sesstionType int32, content string) error {
|
||||
var resp sdkws.UserSendMsgResp
|
||||
text := sdk_struct.TextElem{Content: content}
|
||||
clientMsgID := utils.GetMsgID(b.userID)
|
||||
msg := &sdkws.MsgData{
|
||||
SendID: b.userID,
|
||||
GroupID: groupID,
|
||||
RecvID: userID,
|
||||
SessionType: sesstionType,
|
||||
ContentType: constant.Text,
|
||||
SenderNickname: b.userID,
|
||||
Content: []byte(utils.StructToJsonString(text)),
|
||||
CreateTime: time.Now().UnixMilli(),
|
||||
SenderPlatformID: constant.AdminPlatformID,
|
||||
ClientMsgID: clientMsgID,
|
||||
}
|
||||
now := time.Now().UnixMilli()
|
||||
if err := b.longConnMgr.SendReqWaitResp(ctx, msg, constant.SendMsg, &resp); err != nil {
|
||||
b.failedMessageMap[clientMsgID] = err
|
||||
|
||||
return err
|
||||
}
|
||||
if resp.SendTime-now > 1500 {
|
||||
log.ZWarn(ctx, "msg recv resp is too slow", nil, "sendTime", resp.SendTime, "now", now)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *SendMsgUser) recvPushMsg(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case cmd := <-b.pushMsgAndMaxSeqCh:
|
||||
switch cmd.Cmd {
|
||||
case constant.CmdPushMsg:
|
||||
pushMsgs := cmd.Value.(*sdkws.PushMessages)
|
||||
for _, push := range pushMsgs.Msgs {
|
||||
for _, msg := range push.Msgs {
|
||||
if b.recvPushMsgCallback == nil {
|
||||
b.defaultRecvPushMsgCallback(msg)
|
||||
} else {
|
||||
b.recvPushMsgCallback(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *SendMsgUser) defaultRecvPushMsgCallback(msg *sdkws.MsgData) {
|
||||
}
|
||||
|
||||
type ConnListner struct {
|
||||
}
|
||||
|
||||
func (c *ConnListner) OnConnecting() {}
|
||||
func (c *ConnListner) OnConnectSuccess() {}
|
||||
func (c *ConnListner) OnConnectFailed(errCode int32, errMsg string) {
|
||||
// log.ZError(context.Background(), "connect failed", nil, "errCode", errCode, "errMsg", errMsg)
|
||||
}
|
||||
func (c *ConnListner) OnKickedOffline() {}
|
||||
func (c *ConnListner) OnUserTokenExpired() {}
|
||||
43
go/chao-sdk-core/msgtest/module/user_manager.go
Normal file
43
go/chao-sdk-core/msgtest/module/user_manager.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package module
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/openimsdk/openim-sdk-core/v3/pkg/constant"
|
||||
"time"
|
||||
|
||||
"github.com/openimsdk/protocol/sdkws"
|
||||
userPB "github.com/openimsdk/protocol/user"
|
||||
)
|
||||
|
||||
type TestUserManager struct {
|
||||
*MetaManager
|
||||
}
|
||||
|
||||
func (t *TestUserManager) GenUserIDs(num int) (userIDs []string) {
|
||||
for i := 0; i < num; i++ {
|
||||
userIDs = append(userIDs, fmt.Sprintf("testv3new_%d_%d", time.Now().UnixNano(), i))
|
||||
}
|
||||
return userIDs
|
||||
}
|
||||
|
||||
func (t *TestUserManager) GenUserIDsWithPrefix(num int, prefix string) (userIDs []string) {
|
||||
for i := 0; i < num; i++ {
|
||||
userIDs = append(userIDs, fmt.Sprintf("%s_testv3new_%d_%d", prefix, time.Now().UnixNano(), i))
|
||||
}
|
||||
return userIDs
|
||||
}
|
||||
|
||||
func (t *TestUserManager) RegisterUsers(userIDs ...string) error {
|
||||
var users []*sdkws.UserInfo
|
||||
for _, userID := range userIDs {
|
||||
users = append(users, &sdkws.UserInfo{UserID: userID, Nickname: userID})
|
||||
}
|
||||
return t.postWithCtx(constant.UserRegister, &userPB.UserRegisterReq{
|
||||
Secret: t.secret,
|
||||
Users: users,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
func (t *TestUserManager) GetToken(userID string, platformID int32) (string, error) {
|
||||
return t.getToken(userID, platformID)
|
||||
}
|
||||
Reference in New Issue
Block a user