Handle out of order edits

This commit is contained in:
Andrea Maria Piana 2021-06-07 13:45:06 +02:00
parent 7bac25c8e0
commit 58a8872c57
8 changed files with 190 additions and 53 deletions

View File

@ -13,6 +13,8 @@ import (
type EditMessage struct {
protobuf.EditMessage
ID string `json:"id",omitempty"`
// From is a public key of the author of the edit reaction.
From string `json:"from,omitempty"`

View File

@ -512,7 +512,49 @@ func (m *MessageHandler) handleWrappedCommunityDescriptionMessage(payload []byte
return m.communitiesManager.HandleWrappedCommunityDescriptionMessage(payload)
}
func (m *MessageHandler) HandleEditMessage(state *ReceivedMessageState) error {
func (m *Messenger) HandleEditMessage(response *MessengerResponse, editMessage EditMessage) error {
messageID := editMessage.MessageId
// Check if it's already in the response
originalMessage := response.GetMessage(messageID)
// otherwise pull from database
if originalMessage == nil {
var err error
originalMessage, err = m.persistence.MessageByID(messageID)
if err != nil {
return err
}
}
// We don't have the original message, save the edited message
if originalMessage == nil {
return m.persistence.SaveEdit(editMessage)
}
chat, ok := m.allChats.Load(originalMessage.LocalChatID)
if !ok {
return errors.New("chat not found")
}
// Check edit is valid
if originalMessage.From != editMessage.From {
return errors.New("invalid edit, not the right author")
}
// Check that edit should be applied
if originalMessage.EditedAt >= editMessage.Clock {
return m.persistence.SaveEdit(editMessage)
}
// Update message and return it
err := m.applyEditMessage(&editMessage.EditMessage, originalMessage)
if err != nil {
return err
}
response.AddMessage(originalMessage)
response.AddChat(chat)
return nil
}
@ -1114,37 +1156,6 @@ func (m *MessageHandler) HandleChatIdentity(state *ReceivedMessageState, ci prot
return nil
}
func (m *MessageHandler) handleEditedMessage(state *ReceivedMessageState, message *protobuf.EditMessage) (bool, error) {
/*
originalMessageID := message.OriginalMessageId
// Check if it's already in the response
originalMessage := state.Response.GetMessage(originalMessageID)
// otherwise pull from database
if originalMessage == nil {
originalMessage, err := m.persistence.MessageByID(originalMessageID)
if err != nil {
return false, err
}
}
// We don't have the original message, save the edited message
if originalMessage == nil {
// Save edit and return
//m.persistence.SaveMessageEdit()
return false, nil
}
// Check edit is valid
// Check that edit should be applied
// Update message and return it */
return true, nil
}
func (m *MessageHandler) checkForEdits(message *common.Message) error {
// Check for any pending edit
// If any pending edits are available and valid, apply them

View File

@ -326,7 +326,7 @@ func (db sqlitePersistence) tableUserMessagesAllValues(message *common.Message)
command.CommandState,
command.Signature,
message.Replace,
message.EditedAt,
int64(message.EditedAt),
message.RTL,
message.LineCount,
message.ResponseTo,
@ -1427,6 +1427,11 @@ func (db sqlitePersistence) deactivateChat(chat *Chat, currentClockValue uint64,
return db.clearHistory(chat, currentClockValue, tx, true)
}
func (db sqlitePersistence) SaveEdit(editMessage EditMessage) error {
_, err := db.db.Exec(`INSERT INTO user_messages_edits (clock, chat_id, message_id, source, text, id) VALUES(?,?,?,?,?,?)`, editMessage.Clock, editMessage.ChatId, editMessage.MessageId, editMessage.Text, editMessage.From, editMessage.ID)
return err
}
func (db sqlitePersistence) clearHistory(chat *Chat, currentClockValue uint64, tx *sql.Tx, deactivate bool) error {
// Set deleted at clock value if it's not a public chat so that
// old messages will be discarded, or if it's a straight clear history

View File

@ -2460,6 +2460,7 @@ func (r *ReceivedMessageState) addNewActivityCenterNotification(publicKey ecdsa.
}
func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filter][]*types.Message) (*MessengerResponse, error) {
response := &MessengerResponse{}
messageState := &ReceivedMessageState{
AllChats: m.allChats,
AllContacts: m.allContacts,
@ -2469,7 +2470,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
ExistingMessagesMap: make(map[string]bool),
EmojiReactions: make(map[string]*EmojiReaction),
GroupChatInvitations: make(map[string]*GroupChatInvitation),
Response: &MessengerResponse{},
Response: response,
Timesource: m.getTimesource(),
}
@ -2563,6 +2564,22 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
continue
}
case protobuf.EditMessage:
logger.Debug("Handling EditMessage")
editProto := msg.ParsedMessage.Interface().(protobuf.EditMessage)
editMessage := EditMessage{
EditMessage: editProto,
From: contact.ID,
ID: messageID,
SigPubKey: publicKey,
}
err = m.HandleEditMessage(response, editMessage)
if err != nil {
logger.Warn("failed to handle EditMessage", zap.Error(err))
allMessagesProcessed = false
continue
}
case protobuf.PinMessage:
pinMessage := msg.ParsedMessage.Interface().(protobuf.PinMessage)
err = m.handler.HandlePinMessage(messageState, pinMessage)

View File

@ -11,6 +11,7 @@ import (
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/waku"
@ -121,5 +122,101 @@ func (s *MessengerEditMessageSuite) TestEditMessage() {
}
_, err = s.m.EditMessage(context.Background(), editedMessage)
s.Require().Equal(ErrInvalidEditAuthor, err.Error())
s.Require().Equal(ErrInvalidEditAuthor, err)
}
func (s *MessengerEditMessageSuite) TestEditMessageEdgeCases() {
theirMessenger := s.newMessenger()
_, err := theirMessenger.Start()
s.Require().NoError(err)
theirChat := CreateOneToOneChat("Their 1TO1", &s.privateKey.PublicKey, s.m.transport)
err = theirMessenger.SaveChat(theirChat)
s.Require().NoError(err)
ourChat := CreateOneToOneChat("Our 1TO1", &theirMessenger.identity.PublicKey, s.m.transport)
err = s.m.SaveChat(ourChat)
s.Require().NoError(err)
inputMessage := buildTestMessage(*theirChat)
sendResponse, err := theirMessenger.SendChatMessage(context.Background(), inputMessage)
s.NoError(err)
s.Require().Len(sendResponse.Messages(), 1)
response, err := WaitOnMessengerResponse(
s.m,
func(r *MessengerResponse) bool { return len(r.messages) > 0 },
"no messages",
)
s.Require().NoError(err)
s.Require().Len(response.Chats(), 1)
s.Require().Len(response.Messages(), 1)
chat := response.Chats()[0]
editedMessage := sendResponse.Messages()[0]
newContactKey, err := crypto.GenerateKey()
s.Require().NoError(err)
wrongContact, err := BuildContactFromPublicKey(&newContactKey.PublicKey)
s.Require().NoError(err)
editMessage := EditMessage{
EditMessage: protobuf.EditMessage{
Clock: editedMessage.Clock + 1,
Text: "some text",
MessageId: editedMessage.ID,
ChatId: chat.ID,
},
From: wrongContact.ID,
}
response = &MessengerResponse{}
err = s.m.HandleEditMessage(response, editMessage)
// It should error as the user can't edit this message
s.Require().Error(err)
// Edit with a newer clock value
response = &MessengerResponse{}
contact, err := BuildContactFromPublicKey(&theirMessenger.identity.PublicKey)
s.Require().NoError(err)
editMessage = EditMessage{
EditMessage: protobuf.EditMessage{
Clock: editedMessage.Clock + 2,
Text: "some text",
MessageId: editedMessage.ID,
ChatId: chat.ID,
},
From: contact.ID,
}
err = s.m.HandleEditMessage(response, editMessage)
// It should error as the user can't edit this message
s.Require().NoError(err)
// It save the edit
s.Require().Len(response.Messages(), 1)
editedMessage = response.Messages()[0]
// In-between edit
editMessage = EditMessage{
EditMessage: protobuf.EditMessage{
Clock: editedMessage.Clock + 1,
Text: "some other text",
MessageId: editedMessage.ID,
ChatId: chat.ID,
},
From: contact.ID,
}
response = &MessengerResponse{}
err = s.m.HandleEditMessage(response, editMessage)
// It should error as the user can't edit this message
s.Require().NoError(err)
// It discards the edit
s.Require().Len(response.Messages(), 0)
}

View File

@ -45,19 +45,6 @@ func (m *Messenger) EditMessage(ctx context.Context, request *requests.EditMessa
clock, _ := chat.NextClockAndTimestamp(m.getTimesource())
message.Text = request.Text
message.EditedAt = clock
err = message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
if err != nil {
return nil, err
}
err = m.persistence.SaveMessages([]*common.Message{message})
if err != nil {
return nil, err
}
editMessage := &EditMessage{}
editMessage.Text = request.Text
@ -65,6 +52,11 @@ func (m *Messenger) EditMessage(ctx context.Context, request *requests.EditMessa
editMessage.MessageId = request.ID.String()
editMessage.Clock = clock
err = m.applyEditMessage(&editMessage.EditMessage, message)
if err != nil {
return nil, err
}
encodedMessage, err := m.encodeChatEntity(chat, editMessage)
if err != nil {
return nil, err
@ -86,3 +78,15 @@ func (m *Messenger) EditMessage(ctx context.Context, request *requests.EditMessa
return response, nil
}
func (m *Messenger) applyEditMessage(editMessage *protobuf.EditMessage, message *common.Message) error {
message.Text = editMessage.Text
message.EditedAt = editMessage.Clock
err := message.PrepareContent(common.PubkeyToHex(&m.identity.PublicKey))
if err != nil {
return err
}
return m.persistence.SaveMessages([]*common.Message{message})
}

View File

@ -36,7 +36,7 @@
// 1622464518_set_synced_to_from.up.sql (105B)
// 1622464519_add_chat_description.up.sql (93B)
// 1622622253_add_pinned_by_to_pin_messages.up.sql (52B)
// 1622722745_add_original_message_id.up.sql (254B)
// 1622722745_add_original_message_id.up.sql (273B)
// 1623938329_add_author_activity_center_notification_field.up.sql (66B)
// README.md (554B)
// doc.go (850B)
@ -828,7 +828,7 @@ func _1622622253_add_pinned_by_to_pin_messagesUpSql() (*asset, error) {
return a, nil
}
var __1622722745_add_original_message_idUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\xcc\x41\x0a\xc2\x30\x10\x85\xe1\x7d\x4e\xf1\x96\x0a\xde\xa0\xab\xb1\x1d\x54\x88\x29\x84\xd4\x6d\x28\xcd\xa0\x45\xa5\xd0\x49\xc1\xe3\x4b\x41\x17\x42\x5c\x7f\xef\xfd\x64\x03\x7b\x04\xda\x5b\xc6\xa2\x32\xc7\xa7\xa8\xf6\x57\x51\x50\xd3\xa0\x6e\x6d\x77\x76\x90\x34\x66\x49\xb1\xcf\x38\xb9\xc0\x07\xf6\x95\x31\xb5\x67\x0a\x5c\x7a\xc6\x75\xae\xd8\x18\x60\x78\x4c\xc3\xfd\x7b\x82\x6b\x03\x5c\x67\xed\x6e\x95\x5b\x9f\xe3\x98\x70\x21\x5f\x1f\xe9\xd7\x3e\xa1\x7f\xac\xd3\x32\x0f\x52\xa4\x2c\xaf\x5c\x84\x42\xca\x6c\x2b\xf3\x0e\x00\x00\xff\xff\x78\xbe\xc2\xbb\xfe\x00\x00\x00")
var __1622722745_add_original_message_idUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\xcc\xc1\x8a\x83\x30\x14\x85\xe1\x7d\x9e\xe2\x2c\x15\xe6\x0d\x5c\xdd\xd1\xcb\x8c\x4c\x8c\x43\x88\x05\x57\x41\xcc\xa5\x95\xb6\x08\x26\x42\x1f\xbf\x08\xed\xa2\xa0\xeb\xef\x9c\x9f\xb4\x63\x0b\x47\xdf\x9a\xb1\x46\x59\xfc\x5d\x62\x1c\xce\x12\x41\x55\x85\xb2\xd5\x5d\x63\x20\x61\x4a\x12\xfc\x90\x50\x1b\xc7\x3f\x6c\x0b\xa5\x4a\xcb\xe4\x78\xef\xe9\xb7\x79\x44\xa6\x80\xf1\x36\x8f\xd7\xf7\x09\xa6\x75\x30\x9d\xd6\x5f\x9b\x5c\x86\xe4\xa7\x80\x13\xd9\xf2\x97\x3e\xed\x15\x3a\xe2\x38\xaf\xcb\x28\xbb\x94\xe4\x91\x76\xe1\x20\xf5\x6f\xeb\x86\x6c\x8f\x3f\xee\xb3\x29\xe4\x2a\x2f\xd4\x33\x00\x00\xff\xff\x94\x32\x5e\xe6\x11\x01\x00\x00")
func _1622722745_add_original_message_idUpSqlBytes() ([]byte, error) {
return bindataRead(
@ -843,8 +843,8 @@ func _1622722745_add_original_message_idUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1622722745_add_original_message_id.up.sql", size: 254, mode: os.FileMode(0644), modTime: time.Unix(1624368035, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x12, 0x7e, 0x5e, 0x10, 0xbe, 0xe6, 0xdf, 0xb7, 0xbe, 0xce, 0x67, 0xcf, 0x63, 0xae, 0x4, 0x80, 0xab, 0xc3, 0x74, 0x9, 0x3b, 0x6b, 0x48, 0xa9, 0xd0, 0x79, 0xbe, 0x2d, 0xb7, 0x0, 0x5, 0xfc}}
info := bindataFileInfo{name: "1622722745_add_original_message_id.up.sql", size: 273, mode: os.FileMode(0644), modTime: time.Unix(1624368044, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa8, 0x47, 0x48, 0x84, 0x7a, 0x2f, 0x30, 0x5c, 0x33, 0xa4, 0x42, 0xfb, 0x7d, 0xe1, 0xa6, 0x46, 0x9d, 0x20, 0x19, 0x99, 0x56, 0xbb, 0x9f, 0xd, 0xe4, 0x6b, 0x99, 0x29, 0xe5, 0xef, 0xef, 0x58}}
return a, nil
}

View File

@ -6,5 +6,6 @@ CREATE TABLE user_messages_edits (
message_id VARCHAR NOT NULL,
source VARCHAR NOT NULL,
text VARCHAR NOT NULL,
id VARCHAR NOT NULL
id VARCHAR NOT NULL,
PRIMARY KEY(id)
);