Add idempotent_id to chat and threads services (#64)

* Add idempotent_id to chat and threads services

* Rename
This commit is contained in:
ben-toogood
2021-02-05 11:22:48 +00:00
committed by GitHub
parent b850b61085
commit 2c53892bc6
9 changed files with 235 additions and 156 deletions

View File

@@ -62,14 +62,13 @@ func assertMessagesMatch(t *testing.T, exp, act *pb.Message) {
return
}
// adapt this check so we can reuse the func in testing create, where we don't know the exact id
// which will be generated
// adapt these checks so we can reuse the func in testing create, where we don't know the exact id /
// idempotent_id which will be generated
if len(exp.Id) > 0 {
assert.Equal(t, exp.Id, act.Id)
} else {
assert.NotEmpty(t, act.Id)
}
assert.Equal(t, exp.Text, act.Text)
assert.Equal(t, exp.AuthorId, act.AuthorId)
assert.Equal(t, exp.ChatId, act.ChatId)

View File

@@ -2,6 +2,7 @@ package handler
import (
"context"
"strings"
"github.com/google/uuid"
"github.com/micro/micro/v3/service/errors"
@@ -23,31 +24,40 @@ func (c *Chats) CreateMessage(ctx context.Context, req *pb.CreateMessageRequest,
return ErrMissingText
}
return c.DB.Transaction(func(tx *gorm.DB) error {
// lookup the chat
var conv Chat
if err := tx.Where(&Chat{ID: req.ChatId}).First(&conv).Error; err == gorm.ErrRecordNotFound {
return ErrNotFound
} else if err != nil {
logger.Errorf("Error reading chat: %v", err)
return errors.InternalServerError("DATABASE_ERROR", "Error connecting to database")
}
// lookup the chat
var conv Chat
if err := c.DB.Where(&Chat{ID: req.ChatId}).First(&conv).Error; err == gorm.ErrRecordNotFound {
return ErrNotFound
} else if err != nil {
logger.Errorf("Error reading chat: %v", err)
return errors.InternalServerError("DATABASE_ERROR", "Error connecting to database")
}
// create the message
msg := &Message{
ID: uuid.New().String(),
SentAt: c.Time(),
Text: req.Text,
AuthorID: req.AuthorId,
ChatID: req.ChatId,
}
if err := tx.Create(msg).Error; err != nil {
logger.Errorf("Error creating message: %v", err)
return errors.InternalServerError("DATABASE_ERROR", "Error connecting to database")
}
// serialize the response
// create the message
msg := &Message{
ID: req.Id,
SentAt: c.Time(),
Text: req.Text,
AuthorID: req.AuthorId,
ChatID: req.ChatId,
}
if len(msg.ID) == 0 {
msg.ID = uuid.New().String()
}
if err := c.DB.Create(msg).Error; err == nil {
rsp.Message = msg.Serialize()
return nil
})
} else if !strings.Contains(err.Error(), "messages_pkey") {
logger.Errorf("Error creating message: %v", err)
return errors.InternalServerError("DATABASE_ERROR", "Error connecting to database")
}
// a message already exists with this id
var existing Message
if err := c.DB.Where(&Message{ID: msg.ID}).First(&existing).Error; err != nil {
logger.Errorf("Error creating message: %v", err)
return errors.InternalServerError("DATABASE_ERROR", "Error connecting to database")
}
rsp.Message = existing.Serialize()
return nil
}

View File

@@ -25,12 +25,14 @@ func TestCreateMessage(t *testing.T) {
return
}
iid := uuid.New().String()
tt := []struct {
Name string
AuthorID string
ChatID string
Text string
Error error
ID string
}{
{
Name: "MissingChatID",
@@ -58,18 +60,35 @@ func TestCreateMessage(t *testing.T) {
Error: handler.ErrNotFound,
},
{
Name: "Valid",
Name: "WithoutID",
ChatID: cRsp.Chat.Id,
AuthorID: uuid.New().String(),
Text: "HelloWorld",
},
{
Name: "WithID",
ChatID: cRsp.Chat.Id,
AuthorID: "johndoe",
Text: "HelloWorld",
ID: iid,
},
{
Name: "RepeatID",
ChatID: cRsp.Chat.Id,
AuthorID: "johndoe",
Text: "HelloWorld",
ID: iid,
},
}
for _, tc := range tt {
t.Run(tc.Name, func(t *testing.T) {
var rsp pb.CreateMessageResponse
err := h.CreateMessage(context.TODO(), &pb.CreateMessageRequest{
Text: tc.Text, ChatId: tc.ChatID, AuthorId: tc.AuthorID,
AuthorId: tc.AuthorID,
ChatId: tc.ChatID,
Text: tc.Text,
Id: tc.ID,
}, &rsp)
assert.Equal(t, tc.Error, err)
@@ -83,6 +102,7 @@ func TestCreateMessage(t *testing.T) {
ChatId: tc.ChatID,
SentAt: timestamppb.New(h.Time()),
Text: tc.Text,
Id: tc.ID,
}, rsp.Message)
})
}

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.6.1
// protoc-gen-go v1.23.0
// protoc v3.13.0
// source: proto/chats.proto
package chats
@@ -268,9 +268,10 @@ type CreateMessageRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ChatId string `protobuf:"bytes,1,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
AuthorId string `protobuf:"bytes,2,opt,name=author_id,json=authorId,proto3" json:"author_id,omitempty"`
Text string `protobuf:"bytes,3,opt,name=text,proto3" json:"text,omitempty"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
ChatId string `protobuf:"bytes,2,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
AuthorId string `protobuf:"bytes,3,opt,name=author_id,json=authorId,proto3" json:"author_id,omitempty"`
Text string `protobuf:"bytes,4,opt,name=text,proto3" json:"text,omitempty"`
}
func (x *CreateMessageRequest) Reset() {
@@ -305,6 +306,13 @@ func (*CreateMessageRequest) Descriptor() ([]byte, []int) {
return file_proto_chats_proto_rawDescGZIP(), []int{4}
}
func (x *CreateMessageRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *CreateMessageRequest) GetChatId() string {
if x != nil {
return x.ChatId
@@ -514,13 +522,14 @@ var file_proto_chats_proto_rawDesc = []byte{
0x72, 0x49, 0x64, 0x73, 0x22, 0x35, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x68,
0x61, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x04, 0x63, 0x68,
0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x63, 0x68, 0x61, 0x74, 0x73,
0x2e, 0x43, 0x68, 0x61, 0x74, 0x52, 0x04, 0x63, 0x68, 0x61, 0x74, 0x22, 0x60, 0x0a, 0x14, 0x43,
0x2e, 0x43, 0x68, 0x61, 0x74, 0x52, 0x04, 0x63, 0x68, 0x61, 0x74, 0x22, 0x70, 0x0a, 0x14, 0x43,
0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01,
0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x68, 0x61, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09,
0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78,
0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x41, 0x0a,
0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0x41, 0x0a,
0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x68, 0x61, 0x74, 0x73, 0x2e,

View File

@@ -39,9 +39,10 @@ message CreateChatResponse {
}
message CreateMessageRequest {
string chat_id = 1;
string author_id = 2;
string text = 3;
string id = 1;
string chat_id = 2;
string author_id = 3;
string text = 4;
}
message CreateMessageResponse {