Add SendChatMessages endpoints

This commit adds an endpoint to batch the sending of messages.
This is useful to simplify client logic when sending a batch of messages
and ensuring the correct order in the message stream.

It currently implements only what's needed, and naively return an error
if any of the messages fail.
This commit is contained in:
Andrea Maria Piana 2020-12-01 10:43:46 +01:00
parent 0304b3fa46
commit 156c0de832
6 changed files with 167 additions and 20 deletions

View File

@ -1 +1 @@
0.64.2
0.64.3

11
protocol/errors.go Normal file
View File

@ -0,0 +1,11 @@
package protocol
import (
"github.com/pkg/errors"
)
var (
ErrChatIDEmpty = errors.New("chat ID is empty")
ErrChatNotFound = errors.New("can't find chat")
ErrNotImplemented = errors.New("not implemented")
)

View File

@ -42,12 +42,6 @@ const PubKeyStringLength = 132
const transactionSentTxt = "Transaction sent"
var (
ErrChatIDEmpty = errors.New("chat ID is empty")
ErrChatNotFound = errors.New("can't find chat")
ErrNotImplemented = errors.New("not implemented")
)
// Messenger is a entity managing chats and messages.
// It acts as a bridge between the application and encryption
// layers.
@ -89,19 +83,6 @@ type RawResponse struct {
Messages []*v1protocol.StatusMessage `json:"messages"`
}
type MessengerResponse struct {
Chats []*Chat `json:"chats,omitempty"`
Messages []*common.Message `json:"messages,omitempty"`
Contacts []*Contact `json:"contacts,omitempty"`
Installations []*multidevice.Installation `json:"installations,omitempty"`
EmojiReactions []*EmojiReaction `json:"emojiReactions,omitempty"`
Invitations []*GroupChatInvitation `json:"invitations,omitempty"`
}
func (m *MessengerResponse) IsEmpty() bool {
return len(m.Chats) == 0 && len(m.Messages) == 0 && len(m.Contacts) == 0 && len(m.Installations) == 0 && len(m.Invitations) == 0
}
type dbConfig struct {
dbPath string
dbKey string
@ -1722,7 +1703,32 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec common.RawMessage)
func (m *Messenger) SendChatMessage(ctx context.Context, message *common.Message) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.sendChatMessage(ctx, message)
}
// SendChatMessages takes a array of messages and sends it based on the corresponding chats
func (m *Messenger) SendChatMessages(ctx context.Context, messages []*common.Message) (*MessengerResponse, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
var response MessengerResponse
for _, message := range messages {
messageResponse, err := m.sendChatMessage(ctx, message)
if err != nil {
return nil, err
}
err = response.Merge(messageResponse)
if err != nil {
return nil, err
}
}
return &response, nil
}
// SendChatMessage takes a minimal message and sends it based on the corresponding chat
func (m *Messenger) sendChatMessage(ctx context.Context, message *common.Message) (*MessengerResponse, error) {
if len(message.ImagePath) != 0 {
file, err := os.Open(message.ImagePath)
if err != nil {

View File

@ -0,0 +1,53 @@
package protocol
import (
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/encryption/multidevice"
)
type MessengerResponse struct {
Chats []*Chat `json:"chats,omitempty"`
Messages []*common.Message `json:"messages,omitempty"`
Contacts []*Contact `json:"contacts,omitempty"`
Installations []*multidevice.Installation `json:"installations,omitempty"`
EmojiReactions []*EmojiReaction `json:"emojiReactions,omitempty"`
Invitations []*GroupChatInvitation `json:"invitations,omitempty"`
}
func (m *MessengerResponse) IsEmpty() bool {
return len(m.Chats) == 0 && len(m.Messages) == 0 && len(m.Contacts) == 0 && len(m.Installations) == 0 && len(m.Invitations) == 0
}
func (m *MessengerResponse) Merge(response *MessengerResponse) error {
if len(response.Contacts)+len(response.Installations)+len(response.EmojiReactions)+len(response.Invitations) != 0 {
return ErrNotImplemented
}
for _, overrideChat := range response.Chats {
var found = false
for idx, chat := range m.Chats {
if chat.ID == overrideChat.ID {
m.Chats[idx] = overrideChat
found = true
}
}
if !found {
m.Chats = append(m.Chats, overrideChat)
}
}
for _, overrideMessage := range response.Messages {
var found = false
for idx, chat := range m.Messages {
if chat.ID == overrideMessage.ID {
m.Messages[idx] = overrideMessage
found = true
}
}
if !found {
m.Messages = append(m.Messages, overrideMessage)
}
}
return nil
}

View File

@ -0,0 +1,73 @@
package protocol
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/encryption/multidevice"
)
func TestMessengerResponseMergeChats(t *testing.T) {
chat1 := &Chat{ID: "1"}
modifiedChat1 := &Chat{ID: "1", Name: "name"}
chat2 := &Chat{ID: "3"}
response1 := &MessengerResponse{
Chats: []*Chat{chat1},
}
response2 := &MessengerResponse{
Chats: []*Chat{modifiedChat1, chat2},
}
require.NoError(t, response1.Merge(response2))
require.Len(t, response1.Chats, 2)
require.Equal(t, modifiedChat1, response1.Chats[0])
require.Equal(t, chat2, response1.Chats[1])
}
func TestMessengerResponseMergeMessages(t *testing.T) {
message1 := &common.Message{ID: "1"}
modifiedMessage1 := &common.Message{ID: "1", From: "name"}
message2 := &common.Message{ID: "3"}
response1 := &MessengerResponse{
Messages: []*common.Message{message1},
}
response2 := &MessengerResponse{
Messages: []*common.Message{modifiedMessage1, message2},
}
require.NoError(t, response1.Merge(response2))
require.Len(t, response1.Messages, 2)
require.Equal(t, modifiedMessage1, response1.Messages[0])
require.Equal(t, message2, response1.Messages[1])
}
func TestMessengerResponseMergeNotImplemented(t *testing.T) {
response1 := &MessengerResponse{}
response2 := &MessengerResponse{
Contacts: []*Contact{&Contact{}},
}
require.Error(t, response1.Merge(response2))
response2 = &MessengerResponse{
Installations: []*multidevice.Installation{&multidevice.Installation{}},
}
require.Error(t, response1.Merge(response2))
response2 = &MessengerResponse{
EmojiReactions: []*EmojiReaction{&EmojiReaction{}},
}
require.Error(t, response1.Merge(response2))
response2 = &MessengerResponse{
Invitations: []*GroupChatInvitation{&GroupChatInvitation{}},
}
require.Error(t, response1.Merge(response2))
}

View File

@ -365,6 +365,10 @@ func (api *PublicAPI) ReSendChatMessage(ctx context.Context, messageID string) e
return api.service.messenger.ReSendChatMessage(ctx, messageID)
}
func (api *PublicAPI) SendChatMessages(ctx context.Context, messages []*common.Message) (*protocol.MessengerResponse, error) {
return api.service.messenger.SendChatMessages(ctx, messages)
}
func (api *PublicAPI) RequestTransaction(ctx context.Context, chatID, value, contract, address string) (*protocol.MessengerResponse, error) {
return api.service.messenger.RequestTransaction(ctx, chatID, value, contract, address)
}