fix: pagination, adds total pages count and total rows count to results.

This commit is contained in:
Abhinav Raut
2024-11-03 01:35:16 +05:30
parent ed314eb1a5
commit 4f4d79409c
10 changed files with 119 additions and 84 deletions
+29 -25
View File
@@ -289,59 +289,48 @@ func (c *Manager) GetConversationUUID(id int) (string, error) {
}
// GetAllConversationsList retrieves all conversations with optional filtering, ordering, and pagination.
func (c *Manager) GetAllConversationsList(order, orderBy, filters string, page, pageSize int) ([]models.Conversation, error) {
func (c *Manager) GetAllConversationsList(order, orderBy, filters string, page, pageSize int) ([]models.Conversation, int, error) {
return c.GetConversations(0, models.AllConversations, order, orderBy, filters, page, pageSize)
}
// GetAssignedConversationsList retrieves conversations assigned to a specific user with optional filtering, ordering, and pagination.
func (c *Manager) GetAssignedConversationsList(userID int, order, orderBy, filters string, page, pageSize int) ([]models.Conversation, error) {
func (c *Manager) GetAssignedConversationsList(userID int, order, orderBy, filters string, page, pageSize int) ([]models.Conversation, int, error) {
return c.GetConversations(userID, models.AssignedConversations, order, orderBy, filters, page, pageSize)
}
// GetUnassignedConversationsList retrieves conversations assigned to a team the user is part of with optional filtering, ordering, and pagination.
func (c *Manager) GetUnassignedConversationsList(userID int, order, orderBy, filters string, page, pageSize int) ([]models.Conversation, error) {
func (c *Manager) GetUnassignedConversationsList(userID int, order, orderBy, filters string, page, pageSize int) ([]models.Conversation, int, error) {
return c.GetConversations(userID, models.UnassignedConversations, order, orderBy, filters, page, pageSize)
}
// GetConversations retrieves conversations list based on user ID, type, and optional filtering, ordering, and pagination.
func (c *Manager) GetConversations(userID int, listType, order, orderBy, filters string, page, pageSize int) ([]models.Conversation, error) {
func (c *Manager) GetConversations(userID int, listType, order, orderBy, filters string, page, pageSize int) ([]models.Conversation, int, error) {
var conversations = make([]models.Conversation, 0)
if orderBy == "" {
orderBy = "last_message_at"
}
if order == "" {
order = "DESC"
}
if filters == "" {
filters = "[]"
}
query, qArgs, err := c.makeConversationsListQuery(userID, c.q.GetConversations, listType, order, orderBy, page, pageSize, filters)
query, pageSize, qArgs, err := c.makeConversationsListQuery(userID, c.q.GetConversations, listType, order, orderBy, page, pageSize, filters)
if err != nil {
return conversations, envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.entities.conversations}"), nil)
return conversations, pageSize, envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.entities.conversations}"), nil)
}
tx, err := c.db.BeginTxx(context.Background(), nil)
defer tx.Rollback()
if err != nil {
c.lo.Error("error preparing get conversations query", "error", err)
return conversations, envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.entities.conversations}"), nil)
return conversations, pageSize, envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.entities.conversations}"), nil)
}
if err := tx.Select(&conversations, query, qArgs...); err != nil {
c.lo.Error("error fetching conversations", "error", err)
return conversations, envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.entities.conversations}"), nil)
return conversations, pageSize, envelope.NewError(envelope.GeneralError, c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.entities.conversations}"), nil)
}
return conversations, nil
return conversations, pageSize, nil
}
// GetConversationsListUUIDs retrieves the UUIDs of conversations list.
func (c *Manager) GetConversationsListUUIDs(userID, page, pageSize int, typ string) ([]string, error) {
var ids = make([]string, 0)
query, qArgs, err := c.makeConversationsListQuery(userID, c.q.GetConversationsListUUIDs, typ, "", "", page, pageSize, "")
query, _, qArgs, err := c.makeConversationsListQuery(userID, c.q.GetConversationsListUUIDs, typ, "", "", page, pageSize, "")
if err != nil {
c.lo.Error("error generating conversations query", "error", err)
return ids, err
@@ -560,12 +549,27 @@ func (t *Manager) UpsertConversationTags(uuid string, tagIDs []int) error {
}
// makeConversationsListQuery prepares a SQL query string for conversations list
func (c *Manager) makeConversationsListQuery(userID int, baseQuery, listType, order, orderBy string, page, pageSize int, filtersJSON string) (string, []interface{}, error) {
func (c *Manager) makeConversationsListQuery(userID int, baseQuery, listType, order, orderBy string, page, pageSize int, filtersJSON string) (string, int, []interface{}, error) {
var qArgs []interface{}
if orderBy == "" {
orderBy = "last_message_at"
}
if order == "" {
order = "DESC"
}
if filtersJSON == "" {
filtersJSON = "[]"
}
if pageSize > conversationsListMaxPageSize {
pageSize = conversationsListMaxPageSize
}
if pageSize < 1 {
pageSize = 10
}
if page < 1 {
page = 1
}
// Set condition based on the list type.
switch listType {
@@ -578,7 +582,7 @@ func (c *Manager) makeConversationsListQuery(userID int, baseQuery, listType, or
case models.AllConversations:
baseQuery = fmt.Sprintf(baseQuery, "")
default:
return "", nil, fmt.Errorf("invalid conversation type %s", listType)
return "", pageSize, nil, fmt.Errorf("invalid conversation type %s", listType)
}
query, qArgs, err := dbutil.PaginateAndFilterQuery(baseQuery, qArgs, dbutil.PaginationOptions{
@@ -591,10 +595,10 @@ func (c *Manager) makeConversationsListQuery(userID int, baseQuery, listType, or
})
if err != nil {
c.lo.Error("error preparing query", "error", err)
return "", nil, err
return "", pageSize, nil, err
}
return query, qArgs, err
return query, pageSize, qArgs, err
}
// GetToAddress retrieves the recipient addresses for a conversation.
+12 -10
View File
@@ -212,32 +212,32 @@ func (m *Manager) RenderContentInTemplate(channel string, message *models.Messag
}
// GetConversationMessages retrieves messages for a specific conversation.
func (m *Manager) GetConversationMessages(conversationUUID string, page, pageSize int) ([]models.Message, error) {
func (m *Manager) GetConversationMessages(conversationUUID string, page, pageSize int) ([]models.Message, int, error) {
var (
messages = make([]models.Message, 0)
qArgs []interface{}
)
qArgs = append(qArgs, conversationUUID)
query, qArgs, err := m.generateMessagesQuery(m.q.GetMessages, qArgs, page, pageSize)
query, pageSize, qArgs, err := m.generateMessagesQuery(m.q.GetMessages, qArgs, page, pageSize)
if err != nil {
m.lo.Error("error generating messages query", "error", err)
return messages, envelope.NewError(envelope.GeneralError, "Error fetching messages", nil)
return messages, pageSize, envelope.NewError(envelope.GeneralError, "Error fetching messages", nil)
}
tx, err := m.db.BeginTxx(context.Background(), nil)
defer tx.Rollback()
if err != nil {
m.lo.Error("error preparing get messages query", "error", err)
return messages, envelope.NewError(envelope.GeneralError, "Error fetching messages", nil)
return messages, pageSize, envelope.NewError(envelope.GeneralError, "Error fetching messages", nil)
}
if err := tx.Select(&messages, query, qArgs...); err != nil {
m.lo.Error("error fetching conversations", "error", err)
return messages, envelope.NewError(envelope.GeneralError, "Error fetching messages", nil)
return messages, pageSize, envelope.NewError(envelope.GeneralError, "Error fetching messages", nil)
}
return messages, nil
return messages, pageSize, nil
}
// GetMessage retrieves a message by UUID.
@@ -489,14 +489,16 @@ func (m *Manager) GetConversationByMessageID(id int) (models.Conversation, error
}
// generateMessagesQuery generates the SQL query for fetching messages in a conversation.
func (c *Manager) generateMessagesQuery(baseQuery string, qArgs []interface{}, page, pageSize int) (string, []interface{}, error) {
// Set default values for page and page size if they are invalid
func (c *Manager) generateMessagesQuery(baseQuery string, qArgs []interface{}, page, pageSize int) (string, int, []interface{}, error) {
if page <= 0 {
page = 1
}
if pageSize <= 0 || pageSize > maxMessagesPerPage {
if pageSize > maxMessagesPerPage {
pageSize = maxMessagesPerPage
}
if pageSize <= 0 {
pageSize = 10
}
// Calculate the offset
offset := (page - 1) * pageSize
@@ -506,7 +508,7 @@ func (c *Manager) generateMessagesQuery(baseQuery string, qArgs []interface{}, p
// Include LIMIT and OFFSET in the SQL query
sqlQuery := fmt.Sprintf(baseQuery, fmt.Sprintf("LIMIT $%d OFFSET $%d", len(qArgs)-1, len(qArgs)))
return sqlQuery, qArgs, nil
return sqlQuery, pageSize, qArgs, nil
}
// uploadMessageAttachments uploads attachments for a message.
+2
View File
@@ -44,6 +44,7 @@ type Conversation struct {
LastMessageAt null.Time `db:"last_message_at" json:"last_message_at"`
LastMessage string `db:"last_message" json:"last_message"`
Contact cmodels.Contact `db:"contact" json:"contact"`
Total int `db:"total" json:"-"`
}
type ConversationParticipant struct {
@@ -93,6 +94,7 @@ type Message struct {
InReplyTo string `json:"-"`
Headers textproto.MIMEHeader `json:"-"`
Media []mmodels.Media `db:"-" json:"-"`
Total int `db:"total" json:"-"`
}
// IncomingMessage links a message with the contact information and inbox id.
+2
View File
@@ -9,6 +9,7 @@ RETURNING id, uuid;
-- name: get-conversations
SELECT
COUNT(*) OVER() AS total,
conversations.updated_at,
conversations.uuid,
conversations.assignee_last_seen_at,
@@ -368,6 +369,7 @@ attachments AS (
GROUP BY message_id
)
SELECT
COUNT(*) OVER() AS total,
m.created_at,
m.updated_at,
m.status,
-8
View File
@@ -47,14 +47,6 @@ func PaginateAndFilterQuery(baseQuery string, existingArgs []interface{}, opts P
return "", nil, err
}
// Validate and set default values for pagination
if opts.Page <= 0 {
opts.Page = 1
}
if opts.PageSize <= 0 {
opts.PageSize = 10 // Default page size
}
// Calculate offset
offset := (opts.Page - 1) * opts.PageSize
+9
View File
@@ -32,6 +32,15 @@ func (e Error) Error() string {
return e.Message
}
// PageResults is a generic struct for paginated results.
type PageResults struct {
Results interface{} `json:"results"`
Total int `json:"total"`
PerPage int `json:"per_page"`
TotalPages int `json:"total_pages"`
Page int `json:"page"`
}
// NewError creates and returns a new instance of Error with custom error metadata.
func NewError(etype string, message string, data interface{}) error {
err := Error{