Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add before/after cursors to getMessages #4590

Merged
merged 3 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions comms/db/queries/get_chat_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ SELECT chat_message.message_id, chat_message.chat_id, chat_message.user_id, chat
FROM chat_message
JOIN chat_member ON chat_message.chat_id = chat_member.chat_id
LEFT JOIN chat_message_reactions reactions ON chat_message.message_id = reactions.message_id
WHERE chat_member.user_id = $1 AND chat_message.chat_id = $2 AND chat_message.created_at < $4 AND (chat_member.cleared_history_at IS NULL OR chat_message.created_at > chat_member.cleared_history_at)
WHERE chat_member.user_id = $1
AND chat_message.chat_id = $2
AND chat_message.created_at < $4
AND chat_message.created_at > $5
AND (chat_member.cleared_history_at IS NULL
OR chat_message.created_at > chat_member.cleared_history_at
)
GROUP BY chat_message.message_id
ORDER BY chat_message.created_at DESC, chat_message.message_id
LIMIT $3
Expand All @@ -41,7 +47,8 @@ type ChatMessagesAndReactionsParams struct {
UserID int32 `db:"user_id" json:"user_id"`
ChatID string `db:"chat_id" json:"chat_id"`
Limit int32 `json:"limit"`
Cursor time.Time `json:"cursor"`
Before time.Time `json:"before"`
After time.Time `json:"after"`
}

type ChatMessageAndReactionsRow struct {
Expand Down Expand Up @@ -98,7 +105,8 @@ func ChatMessagesAndReactions(q db.Queryable, ctx context.Context, arg ChatMessa
arg.UserID,
arg.ChatID,
arg.Limit,
arg.Cursor,
arg.Before,
arg.After,
)
return rows, err
}
Expand Down
26 changes: 16 additions & 10 deletions comms/db/queries/get_summaries.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,38 @@ import (
)

type SummaryRow struct {
TotalCount int64 `db:"total_count" json:"total_count"`
RemainingCount int64 `db:"remaining_count" json:"remaining_count"`
TotalCount int64 `db:"total_count" json:"total_count"`
BeforeCount int64 `db:"before_count" json:"before_count"`
AfterCount int64 `db:"after_count" json:"after_count"`
}

const chatMessagesSummary = `
WITH messages AS (
SELECT
chat_message.message_id, chat_message.chat_id, chat_message.user_id, chat_message.created_at, chat_message.ciphertext
chat_message.message_id, chat_message.created_at
FROM chat_message
JOIN chat_member ON chat_message.chat_id = chat_member.chat_id
WHERE chat_member.user_id = $1 AND chat_message.chat_id = $2 AND (chat_member.cleared_history_at IS NULL OR chat_message.created_at > chat_member.cleared_history_at)
WHERE chat_member.user_id = $1
AND chat_message.chat_id = $2
AND (chat_member.cleared_history_at IS NULL
OR chat_message.created_at > chat_member.cleared_history_at)
)
SELECT
(SELECT COUNT(*) AS total_count FROM messages),
(SELECT COUNT(*) FROM messages WHERE created_at < $3) AS remaining_count
(SELECT COUNT(*) FROM messages WHERE created_at < $3) AS before_count,
(SELECT COUNT(*) FROM messages WHERE created_at > $4) AS after_count
`

type ChatMessagesSummaryParams struct {
UserID int32 `db:"user_id" json:"user_id"`
ChatID string `db:"chat_id" json:"chat_id"`
Cursor time.Time `json:"cursor"`
Before time.Time `json:"before"`
After time.Time `json:"after"`
}

func ChatMessagesSummary(q db.Queryable, ctx context.Context, arg ChatMessagesSummaryParams) (SummaryRow, error) {
var summary SummaryRow
err := q.GetContext(ctx, &summary, chatMessagesSummary, arg.UserID, arg.ChatID, arg.Cursor)
err := q.GetContext(ctx, &summary, chatMessagesSummary, arg.UserID, arg.ChatID, arg.Before, arg.After)
return summary, err
}

Expand All @@ -51,16 +57,16 @@ SELECT
(
SELECT COUNT(*) FROM user_chats
WHERE last_message_at < $1
) AS remaining_count
) AS before_count
`

type UserChatsSummaryParams struct {
Cursor time.Time `json:"cursor"`
Before time.Time `json:"before"`
UserID int32 `db:"user_id" json:"user_id"`
}

func UserChatsSummary(q db.Queryable, ctx context.Context, arg UserChatsSummaryParams) (SummaryRow, error) {
var summary SummaryRow
err := q.GetContext(ctx, &summary, userChatsSummary, arg.Cursor, arg.UserID)
err := q.GetContext(ctx, &summary, userChatsSummary, arg.Before, arg.UserID)
return summary, err
}
4 changes: 2 additions & 2 deletions comms/misc/hashids_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ func DecodeHashId(id string) (int, error) {
return ids[0], err
}
func EncodeHashId(id int) (string, error) {
return hashIdUtil.Encode([]int{ id })
}
return hashIdUtil.Encode([]int{id})
}
40 changes: 26 additions & 14 deletions comms/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,75 +163,87 @@ type Health struct {
}

type Summary struct {
NextCursor string `json:"next_cursor"`
PrevCursor string `json:"prev_cursor"`
RemainingCount float64 `json:"remaining_count"`
TotalCount float64 `json:"total_count"`
NextCount float64 `json:"next_count"`
NextCursor string `json:"next_cursor"`
PrevCount float64 `json:"prev_count"`
PrevCursor string `json:"prev_cursor"`
TotalCount float64 `json:"total_count"`
}

type ChatCreateRPCMethod string

const (
MethodChatCreate ChatCreateRPCMethod = "chat.create"
)

type ChatDeleteRPCMethod string

const (
MethodChatDelete ChatDeleteRPCMethod = "chat.delete"
)

type ChatInviteRPCMethod string

const (
MethodChatInvite ChatInviteRPCMethod = "chat.invite"
)

type ChatMessageRPCMethod string

const (
MethodChatMessage ChatMessageRPCMethod = "chat.message"
)

type ChatReactRPCMethod string

const (
MethodChatReact ChatReactRPCMethod = "chat.react"
)

type ChatReadRPCMethod string

const (
MethodChatRead ChatReadRPCMethod = "chat.read"
)

type ChatBlockRPCMethod string

const (
MethodChatBlock ChatBlockRPCMethod = "chat.block"
)

type ChatUnblockRPCMethod string

const (
MethodChatUnblock ChatUnblockRPCMethod = "chat.unblock"
)

type ChatPermitRPCMethod string

const (
MethodChatPermit ChatPermitRPCMethod = "chat.permit"
)

// Defines who the user allows to message them
type ChatPermission string

const (
All ChatPermission = "all"
All ChatPermission = "all"
Followees ChatPermission = "followees"
None ChatPermission = "none"
Tippers ChatPermission = "tippers"
None ChatPermission = "none"
Tippers ChatPermission = "tippers"
)

type RPCMethod string

const (
RPCMethodChatBlock RPCMethod = "chat.block"
RPCMethodChatCreate RPCMethod = "chat.create"
RPCMethodChatDelete RPCMethod = "chat.delete"
RPCMethodChatInvite RPCMethod = "chat.invite"
RPCMethodChatBlock RPCMethod = "chat.block"
RPCMethodChatCreate RPCMethod = "chat.create"
RPCMethodChatDelete RPCMethod = "chat.delete"
RPCMethodChatInvite RPCMethod = "chat.invite"
RPCMethodChatMessage RPCMethod = "chat.message"
RPCMethodChatPermit RPCMethod = "chat.permit"
RPCMethodChatReact RPCMethod = "chat.react"
RPCMethodChatRead RPCMethod = "chat.read"
RPCMethodChatPermit RPCMethod = "chat.permit"
RPCMethodChatReact RPCMethod = "chat.react"
RPCMethodChatRead RPCMethod = "chat.read"
RPCMethodChatUnblock RPCMethod = "chat.unblock"
)
3 changes: 2 additions & 1 deletion comms/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,9 @@ export type CommsResponse = {
}
summary?: {
prev_cursor: string
prev_count: number
next_cursor: string
remaining_count: number
next_count: number
total_count: number
}
// Overridden in client types but left as any for the server.
Expand Down
10 changes: 6 additions & 4 deletions comms/server/response_mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ func ToChatResponse(chat queries.UserChatRow, members []db.ChatMember) schema.Us
return chatData
}

func ToSummaryResponse(cursor string, summary queries.SummaryRow) schema.Summary {
func ToSummaryResponse(prevCursor string, nextCursor string, summary queries.SummaryRow) schema.Summary {
responseSummary := schema.Summary{
TotalCount: float64(summary.TotalCount),
RemainingCount: float64(summary.RemainingCount),
NextCursor: cursor,
TotalCount: float64(summary.TotalCount),
PrevCount: float64(summary.BeforeCount),
PrevCursor: prevCursor,
NextCount: float64(summary.AfterCount),
NextCursor: nextCursor,
}
return responseSummary
}
Expand Down
37 changes: 24 additions & 13 deletions comms/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,18 @@ func getChats(c echo.Context) error {
}
responseData[i] = ToChatResponse(chats[i], members)
}
cursorPos := time.Now().UTC()
// TODO: Get these from params and filter queries.UserChats similarly
beforeCursorPos := time.Now().UTC()
afterCursorPos := time.Now().UTC()
if len(chats) > 0 {
lastChat := chats[len(chats)-1]
cursorPos = lastChat.LastMessageAt
beforeCursorPos = chats[len(chats)-1].LastMessageAt
afterCursorPos = chats[0].LastMessageAt
}
summary, err := queries.UserChatsSummary(db.Conn, ctx, queries.UserChatsSummaryParams{UserID: userId, Cursor: cursorPos})
summary, err := queries.UserChatsSummary(db.Conn, ctx, queries.UserChatsSummaryParams{UserID: userId, Before: beforeCursorPos})
if err != nil {
return err
}
responseSummary := ToSummaryResponse(cursorPos.Format(time.RFC3339Nano), summary)
responseSummary := ToSummaryResponse(beforeCursorPos.Format(time.RFC3339Nano), afterCursorPos.Format(time.RFC3339Nano), summary)
response := schema.CommsResponse{
Health: getHealthStatus(),
Data: responseData,
Expand Down Expand Up @@ -148,13 +150,20 @@ func getMessages(c echo.Context) error {
return c.String(400, "wallet not found: "+err.Error())
}

params := queries.ChatMessagesAndReactionsParams{UserID: int32(userId), ChatID: c.Param("id"), Cursor: time.Now().UTC(), Limit: 50}
if c.QueryParam("cursor") != "" {
cursor, err := time.Parse(time.RFC3339Nano, c.QueryParam("cursor"))
params := queries.ChatMessagesAndReactionsParams{UserID: int32(userId), ChatID: c.Param("id"), Before: time.Now().UTC(), After: time.Time{}, Limit: 50}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should After also be time.Now().UTC()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so - I think it should default to beginning of time to include as much as possible?

if c.QueryParam("before") != "" {
beforeCursor, err := time.Parse(time.RFC3339Nano, c.QueryParam("before"))
if err != nil {
return err
}
params.Cursor = cursor
params.Before = beforeCursor
}
if c.QueryParam("after") != "" {
afterCursor, err := time.Parse(time.RFC3339Nano, c.QueryParam("after"))
if err != nil {
return err
}
params.After = afterCursor
}
if c.QueryParam("limit") != "" {
limit, err := strconv.Atoi(c.QueryParam("limit"))
Expand All @@ -169,15 +178,17 @@ func getMessages(c echo.Context) error {
return err
}

cursorPos := params.Cursor
beforeCursorPos := params.Before
afterCursorPos := params.After
if len(messages) > 0 {
cursorPos = messages[len(messages)-1].CreatedAt
beforeCursorPos = messages[len(messages)-1].CreatedAt
afterCursorPos = messages[0].CreatedAt
}
summary, err := queries.ChatMessagesSummary(db.Conn, ctx, queries.ChatMessagesSummaryParams{UserID: userId, ChatID: c.Param("id"), Cursor: cursorPos})
summary, err := queries.ChatMessagesSummary(db.Conn, ctx, queries.ChatMessagesSummaryParams{UserID: userId, ChatID: c.Param("id"), Before: beforeCursorPos, After: afterCursorPos})
if err != nil {
return err
}
responseSummary := ToSummaryResponse(cursorPos.Format(time.RFC3339Nano), summary)
responseSummary := ToSummaryResponse(beforeCursorPos.Format(time.RFC3339Nano), afterCursorPos.Format(time.RFC3339Nano), summary)
response := schema.CommsResponse{
Health: getHealthStatus(),
Data: Map(messages, ToMessageResponse),
Expand Down
20 changes: 12 additions & 8 deletions comms/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ func TestGetChats(t *testing.T) {
// Create 2 chats
chatId1 := "chat1"
chatId2 := "chat2"
chat1CreatedAt := time.Now().UTC()
chat2CreatedAt := time.Now().UTC().Add(time.Minute * time.Duration(30))
chat1CreatedAt := time.Now().UTC().Add(-time.Minute * time.Duration(60))
chat2CreatedAt := time.Now().UTC().Add(-time.Minute * time.Duration(30))
_, err = tx.Exec("insert into chat (chat_id, created_at, last_message_at) values ($1, $2, $2), ($3, $4, $4)", chatId1, chat1CreatedAt, chatId2, chat2CreatedAt)
assert.NoError(t, err)

Expand Down Expand Up @@ -171,9 +171,11 @@ func TestGetChats(t *testing.T) {
expectedChat1Data,
}
expectedSummary := schema.Summary{
TotalCount: float64(2),
RemainingCount: float64(0),
NextCursor: chat1CreatedAt.Format(time.RFC3339Nano),
TotalCount: float64(2),
NextCount: float64(0),
NextCursor: chat2CreatedAt.Format(time.RFC3339Nano),
PrevCount: float64(0),
PrevCursor: chat1CreatedAt.Format(time.RFC3339Nano),
}
expectedResponse, err := json.Marshal(
schema.CommsResponse{
Expand Down Expand Up @@ -337,9 +339,11 @@ func TestGetMessages(t *testing.T) {
expectedMessage1Data,
}
expectedSummary := schema.Summary{
TotalCount: float64(2),
RemainingCount: float64(0),
NextCursor: message1CreatedAt.Format(time.RFC3339Nano),
TotalCount: float64(2),
NextCount: float64(0),
NextCursor: message2CreatedAt.Format(time.RFC3339Nano),
PrevCount: float64(0),
PrevCursor: message1CreatedAt.Format(time.RFC3339Nano),
}
expectedResponse, err := json.Marshal(
schema.CommsResponse{
Expand Down
3 changes: 2 additions & 1 deletion libs/src/sdk/api/chats/serverTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,9 @@ export type CommsResponse = {
}
summary?: {
prev_cursor: string
prev_count: number
next_cursor: string
remaining_count: number
next_count: number
total_count: number
}
// Overridden in client types but left as any for the server.
Expand Down