Skip to content

Commit 781f488

Browse files
author
Jeff Yanta
committed
chat: support descriptions
1 parent 9cac3b2 commit 781f488

File tree

11 files changed

+191
-5
lines changed

11 files changed

+191
-5
lines changed

chat/memory/store.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,20 @@ func (s *InMemoryStore) SetDisplayName(ctx context.Context, chatID *commonpb.Cha
262262
return nil
263263
}
264264

265+
func (s *InMemoryStore) SetDescription(ctx context.Context, chatID *commonpb.ChatId, description string) error {
266+
s.mu.Lock()
267+
defer s.mu.Unlock()
268+
269+
md, ok := s.chats[string(chatID.Value)]
270+
if !ok {
271+
return chat.ErrChatNotFound
272+
}
273+
274+
md.Description = description
275+
276+
return nil
277+
}
278+
265279
func (s *InMemoryStore) SetMessagingFee(ctx context.Context, chatID *commonpb.ChatId, messagingFee *commonpb.PaymentAmount) error {
266280
s.mu.Lock()
267281
defer s.mu.Unlock()

chat/postgres/model.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919

2020
const (
2121
chatsTableName = "flipchat_chats"
22-
allChatFields = `"id", "displayName", "roomNumber", "coverCharge", "type", "isOpen", "createdBy", "createdAt", "updatedAt", "lastActivityAt"`
22+
allChatFields = `"id", "displayName", "roomNumber", "coverCharge", "type", "isOpen", "description", "createdBy", "createdAt", "updatedAt", "lastActivityAt"`
2323

2424
membersTableName = "flipchat_members"
2525
allMemberFields = `"chatId", "userId", "addedById", "isMuted", "isPushEnabled", "hasModPermission", "hasSendPermission", "isSoftDeleted", "createdAt", "updatedAt"`
@@ -32,6 +32,7 @@ type chatModel struct {
3232
CoverCharge uint64 `db:"coverCharge"`
3333
Type int `db:"type"`
3434
IsOpen bool `db:"isOpen"`
35+
Descripton *string `db:"description"`
3536
CreatedBy string `db:"createdBy"`
3637
CreatedAt time.Time `db:"createdAt"`
3738
UpdatedAt time.Time `db:"updatedAt"`
@@ -68,6 +69,12 @@ func toChatModel(obj *chatpb.Metadata) (*chatModel, error) {
6869
isOpen = obj.OpenStatus.IsCurrentlyOpen
6970
}
7071

72+
var description *string
73+
if len(obj.Description) > 0 {
74+
value := obj.Description
75+
description = &value
76+
}
77+
7178
var createdBy string
7279
if obj.Owner != nil {
7380
createdBy = pg.Encode(obj.Owner.Value)
@@ -80,6 +87,7 @@ func toChatModel(obj *chatpb.Metadata) (*chatModel, error) {
8087
CoverCharge: coverCharge,
8188
Type: int(obj.Type),
8289
IsOpen: isOpen,
90+
Descripton: description,
8391
CreatedBy: createdBy,
8492
LastActivityAt: obj.LastActivity.AsTime().UTC(),
8593
}, nil
@@ -112,6 +120,7 @@ func fromChatModel(m *chatModel) (*chatpb.Metadata, error) {
112120
MessagingFee: messagingFee,
113121
Type: chatpb.Metadata_ChatType(m.Type),
114122
OpenStatus: &chatpb.OpenStatus{IsCurrentlyOpen: m.IsOpen},
123+
Description: *pointer.StringOrDefault(m.Descripton, ""),
115124
Owner: owner,
116125
LastActivity: timestamppb.New(m.LastActivityAt),
117126
}, nil
@@ -179,7 +188,7 @@ func (m *chatModel) dbPut(ctx context.Context, pool *pgxpool.Pool) error {
179188
return err
180189
}
181190

182-
putQuery := `INSERT INTO ` + chatsTableName + `(` + allChatFields + `) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW(), $8) RETURNING ` + allChatFields
191+
putQuery := `INSERT INTO ` + chatsTableName + `(` + allChatFields + `) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW(), NOW(), $9) RETURNING ` + allChatFields
183192
err = pgxscan.Get(
184193
ctx,
185194
pool,
@@ -191,6 +200,7 @@ func (m *chatModel) dbPut(ctx context.Context, pool *pgxpool.Pool) error {
191200
m.CoverCharge,
192201
m.Type,
193202
m.IsOpen,
203+
m.Descripton,
194204
m.CreatedBy,
195205
m.LastActivityAt,
196206
)
@@ -439,6 +449,27 @@ func dbSetDisplayName(ctx context.Context, pool *pgxpool.Pool, chatID *commonpb.
439449
return nil
440450
}
441451

452+
func dbSetDescription(ctx context.Context, pool *pgxpool.Pool, chatID *commonpb.ChatId, description string) error {
453+
query := `UPDATE ` + chatsTableName + ` SET "description" = $1, "updatedAt" = NOW() WHERE "id" = $2`
454+
queryParameters := []any{description, pg.Encode(chatID.Value)}
455+
if len(description) == 0 {
456+
query = `UPDATE ` + chatsTableName + ` SET "description" = NULL, "updatedAt" = NOW() WHERE "id" = $1`
457+
queryParameters = []any{pg.Encode(chatID.Value)}
458+
}
459+
res, err := pool.Exec(
460+
ctx,
461+
query,
462+
queryParameters...,
463+
)
464+
if err != nil {
465+
return err
466+
}
467+
if res.RowsAffected() == 0 {
468+
return chat.ErrChatNotFound
469+
}
470+
return nil
471+
}
472+
442473
func dbSetMessagingFee(ctx context.Context, pool *pgxpool.Pool, chatID *commonpb.ChatId, messagingFee *commonpb.PaymentAmount) error {
443474
query := `UPDATE ` + chatsTableName + ` SET "coverCharge" = $1, "updatedAt" = NOW() WHERE "id" = $2`
444475
res, err := pool.Exec(

chat/postgres/store.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ func (s *store) SetDisplayName(ctx context.Context, chatID *commonpb.ChatId, dis
153153
return dbSetDisplayName(ctx, s.pool, chatID, displayName)
154154
}
155155

156+
func (s *store) SetDescription(ctx context.Context, chatID *commonpb.ChatId, description string) error {
157+
return dbSetDescription(ctx, s.pool, chatID, description)
158+
}
159+
156160
func (s *store) SetMessagingFee(ctx context.Context, chatID *commonpb.ChatId, messagingFee *commonpb.PaymentAmount) error {
157161
return dbSetMessagingFee(ctx, s.pool, chatID, messagingFee)
158162
}

chat/server.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,58 @@ func (s *Server) SetDisplayName(ctx context.Context, req *chatpb.SetDisplayNameR
941941
return &chatpb.SetDisplayNameResponse{}, nil
942942
}
943943

944+
func (s *Server) SetDescription(ctx context.Context, req *chatpb.SetDescriptionRequest) (*chatpb.SetDescriptionResponse, error) {
945+
userID, err := s.authz.Authorize(ctx, req, &req.Auth)
946+
if err != nil {
947+
return nil, err
948+
}
949+
950+
log := s.log.With(
951+
zap.String("user_id", model.UserIDString(userID)),
952+
zap.String("chat_id", base64.StdEncoding.EncodeToString(req.ChatId.Value)),
953+
)
954+
955+
md, err := s.getMetadata(ctx, req.ChatId, nil)
956+
if err == ErrChatNotFound {
957+
return &chatpb.SetDescriptionResponse{Result: chatpb.SetDescriptionResponse_DENIED}, nil
958+
} else if err != nil {
959+
log.Warn("Failed to get chat", zap.Error(err))
960+
return nil, status.Errorf(codes.Internal, "failed to get chat")
961+
}
962+
963+
if md.Type != chatpb.Metadata_GROUP {
964+
return &chatpb.SetDescriptionResponse{Result: chatpb.SetDescriptionResponse_DENIED}, nil
965+
}
966+
if md.Owner == nil || !bytes.Equal(md.Owner.Value, userID.Value) {
967+
return &chatpb.SetDescriptionResponse{Result: chatpb.SetDescriptionResponse_DENIED}, nil
968+
}
969+
970+
if md.Description == req.Description {
971+
return &chatpb.SetDescriptionResponse{}, nil
972+
}
973+
974+
err = s.chats.SetDescription(ctx, req.ChatId, req.Description)
975+
if err != nil {
976+
log.Warn("Failed to set description", zap.Error(err))
977+
return nil, status.Errorf(codes.Internal, "failed to set description")
978+
}
979+
980+
err = s.eventBus.OnEvent(req.ChatId, &event.ChatEvent{ChatID: md.ChatId, MetadataUpdates: []*chatpb.MetadataUpdate{
981+
{
982+
Kind: &chatpb.MetadataUpdate_DescriptionChanged_{
983+
DescriptionChanged: &chatpb.MetadataUpdate_DescriptionChanged{
984+
NewDescription: req.Description,
985+
},
986+
},
987+
},
988+
}})
989+
if err != nil {
990+
s.log.Warn("Failed to notify description changed", zap.Error(err))
991+
}
992+
993+
return &chatpb.SetDescriptionResponse{}, nil
994+
}
995+
944996
func (s *Server) SetMessagingFee(ctx context.Context, req *chatpb.SetMessagingFeeRequest) (*chatpb.SetMessagingFeeResponse, error) {
945997
userID, err := s.authz.Authorize(ctx, req, &req.Auth)
946998
if err != nil {

chat/store.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ type Store interface {
9595

9696
SetDisplayName(ctx context.Context, chatID *commonpb.ChatId, displayName string) error
9797

98+
SetDescription(ctx context.Context, chatID *commonpb.ChatId, description string) error
99+
98100
SetMessagingFee(ctx context.Context, chatID *commonpb.ChatId, messagingFee *commonpb.PaymentAmount) error
99101

100102
SetOpenStatus(ctx context.Context, chatID *commonpb.ChatId, isOpen bool) error

chat/tests/server.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,40 @@ func testServer(
599599
require.Equal(t, setDisplayName.DisplayName, get.Metadata.DisplayName)
600600
})
601601

602+
t.Run("Set description", func(t *testing.T) {
603+
setDescription := &chatpb.SetDescriptionRequest{
604+
ChatId: created.Chat.ChatId,
605+
Description: "Description",
606+
}
607+
require.NoError(t, keyPair.Auth(setDescription, &setDescription.Auth))
608+
609+
setDescriptionResp, err := client.SetDescription(context.Background(), setDescription)
610+
require.NoError(t, err)
611+
require.Equal(t, chatpb.SetDescriptionResponse_OK, setDescriptionResp.Result)
612+
613+
get, err := client.GetChat(context.Background(), getByID)
614+
require.NoError(t, err)
615+
require.Equal(t, chatpb.GetChatResponse_OK, get.Result)
616+
require.Equal(t, setDescription.Description, get.Metadata.Description)
617+
})
618+
619+
t.Run("Remove description", func(t *testing.T) {
620+
setDescription := &chatpb.SetDescriptionRequest{
621+
ChatId: created.Chat.ChatId,
622+
Description: "",
623+
}
624+
require.NoError(t, keyPair.Auth(setDescription, &setDescription.Auth))
625+
626+
setDescriptionResp, err := client.SetDescription(context.Background(), setDescription)
627+
require.NoError(t, err)
628+
require.Equal(t, chatpb.SetDescriptionResponse_OK, setDescriptionResp.Result)
629+
630+
get, err := client.GetChat(context.Background(), getByID)
631+
require.NoError(t, err)
632+
require.Equal(t, chatpb.GetChatResponse_OK, get.Result)
633+
require.Empty(t, get.Metadata.Description)
634+
})
635+
602636
t.Run("Close and open room", func(t *testing.T) {
603637
close := &chatpb.CloseChatRequest{
604638
ChatId: created.Chat.ChatId,
@@ -875,6 +909,21 @@ func testServer(
875909
require.Empty(t, u.MemberUpdates, 0)
876910
require.Equal(t, u.MetadataUpdates[0].GetDisplayNameChanged().NewDisplayName, setDisplayName.DisplayName)
877911

912+
// Other user updates chat description
913+
setDescription := &chatpb.SetDescriptionRequest{
914+
ChatId: startedOther.Chat.ChatId,
915+
Description: "Description",
916+
}
917+
require.NoError(t, keyPair.Auth(setDescription, &setDescription.Auth))
918+
_, err = client.SetDescription(ctx, setDescription)
919+
require.NoError(t, err)
920+
921+
u = <-updateCh
922+
require.NoError(t, protoutil.ProtoEqualError(joined.Metadata.ChatId, u.ChatId))
923+
require.Len(t, u.MetadataUpdates, 1)
924+
require.Empty(t, u.MemberUpdates, 0)
925+
require.Equal(t, u.MetadataUpdates[0].GetDescriptionChanged().NewDescription, setDescription.Description)
926+
878927
// Other user sends messages in the chat
879928
//
880929
// todo: proper integration test with messenger

chat/tests/store.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func RunStoreTests(t *testing.T, s chat.Store, teardown func()) {
3333
testChatStore_JoinLeaveWithPermissions,
3434
testChatStore_AddRemove,
3535
testChatStore_SetDisplayName,
36+
testChatStore_SetDescription,
3637
testChatStore_SetMessagingFee,
3738
testChatStore_AdvanceLastChatActivity,
3839
} {
@@ -53,6 +54,7 @@ func testChatStore_Metadata(t *testing.T, store chat.Store) {
5354
NumUnread: 0,
5455
LastActivity: &timestamppb.Timestamp{Seconds: time.Now().Unix()},
5556
OpenStatus: &chatpb.OpenStatus{IsCurrentlyOpen: true},
57+
Description: "Chat With Description",
5658
}
5759

5860
chatID2 := model.MustGenerateChatID()
@@ -514,6 +516,35 @@ func testChatStore_SetDisplayName(t *testing.T, store chat.Store) {
514516
require.Equal(t, "", result.DisplayName)
515517
}
516518

519+
func testChatStore_SetDescription(t *testing.T, store chat.Store) {
520+
chatID := model.MustGenerateChatID()
521+
522+
require.Equal(t, chat.ErrChatNotFound, store.SetDescription(context.Background(), chatID, "Description"))
523+
524+
_, err := store.CreateChat(context.Background(), &chatpb.Metadata{
525+
ChatId: chatID,
526+
Type: chatpb.Metadata_GROUP,
527+
LastActivity: &timestamppb.Timestamp{Seconds: time.Now().Unix()},
528+
})
529+
require.NoError(t, err)
530+
531+
result, err := store.GetChatMetadata(context.Background(), chatID)
532+
require.NoError(t, err)
533+
require.Empty(t, result.Description)
534+
535+
require.NoError(t, store.SetDescription(context.Background(), chatID, "Description"))
536+
537+
result, err = store.GetChatMetadata(context.Background(), chatID)
538+
require.NoError(t, err)
539+
require.Equal(t, "Description", result.Description)
540+
541+
require.NoError(t, store.SetDescription(context.Background(), chatID, ""))
542+
543+
result, err = store.GetChatMetadata(context.Background(), chatID)
544+
require.NoError(t, err)
545+
require.Equal(t, "", result.Description)
546+
}
547+
517548
func testChatStore_SetMessagingFee(t *testing.T, store chat.Store) {
518549
chatID := model.MustGenerateChatID()
519550

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "flipchat_chats" ADD COLUMN "description" TEXT;

database/prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ model Chat {
101101
coverCharge BigInt @default(0) // todo: rename to messaging fee
102102
type Int @default(0) // ChatType enum: Unknown: 0, TwoWay: 1, Group: 2
103103
isOpen Boolean @default(true)
104+
description String?
104105
105106
createdBy String @default("")
106107
createdAt DateTime @default(now())

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require (
77
github.com/aws/aws-sdk-go-v2 v0.17.0
88
github.com/code-payments/code-protobuf-api v1.18.1-0.20241113145645-72417e3e3c84
99
github.com/code-payments/code-server v1.10.20-0.20250227175955-b835606637cb
10-
github.com/code-payments/flipchat-protobuf-api v1.3.2
10+
github.com/code-payments/flipchat-protobuf-api v1.4.2
1111
github.com/georgysavva/scany/v2 v2.1.3
1212
github.com/google/uuid v1.6.0
1313
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2

0 commit comments

Comments
 (0)