Skip to content

Commit

Permalink
✨ feat: Add pagination support for bookmarks list (main)
Browse files Browse the repository at this point in the history
- Modified SQL query to include total count of bookmarks for pagination
- Updated DAO interface and implementation to return ListBookmarksRow with total count
- Added LoadWithCount method in BookmarkDTO to handle new row structure
- Updated Service layer to return total count along with bookmarks
- Modified HTTP handler to return paginated response with total count
- Added pagination UI components and integrated with bookmarks list
- Updated bookmarks API to return paginated response structure
- Implemented pagination controls in bookmarks list view
  • Loading branch information
vaayne committed Jan 3, 2025
1 parent ee48078 commit 4ffc3e9
Show file tree
Hide file tree
Showing 14 changed files with 465 additions and 106 deletions.
12 changes: 9 additions & 3 deletions database/queries/bookmarks.sql
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@ SELECT * FROM bookmarks WHERE uuid = $1;
SELECT * FROM bookmarks WHERE url = $1 AND user_id = $2;

-- name: ListBookmarks :many
SELECT * FROM bookmarks
WHERE user_id = $1
ORDER BY updated_at DESC
WITH total AS (
SELECT COUNT(*) AS total_count
FROM bookmarks
WHERE user_id = $1
)
SELECT b.*, t.total_count
FROM bookmarks b, total t
WHERE b.user_id = $1
ORDER BY b.updated_at DESC
LIMIT $2 OFFSET $3;

-- name: UpdateBookmark :one
Expand Down
37 changes: 33 additions & 4 deletions docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2680,10 +2680,7 @@ const docTemplate = `{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/bookmarks.BookmarkDTO"
}
"$ref": "#/definitions/httpserver.listBookmarksResponse"
}
}
}
Expand Down Expand Up @@ -4549,9 +4546,18 @@ const docTemplate = `{
"author": {
"type": "string"
},
"cover": {
"type": "string"
},
"description": {
"type": "string"
},
"domain": {
"type": "string"
},
"favicon": {
"type": "string"
},
"highlights": {
"type": "array",
"items": {
Expand All @@ -4564,6 +4570,9 @@ const docTemplate = `{
"published_at": {
"type": "string"
},
"site_name": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
Expand Down Expand Up @@ -4697,6 +4706,26 @@ const docTemplate = `{
}
}
},
"httpserver.listBookmarksResponse": {
"type": "object",
"properties": {
"bookmarks": {
"type": "array",
"items": {
"$ref": "#/definitions/bookmarks.BookmarkDTO"
}
},
"limit": {
"type": "integer"
},
"offset": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
},
"httpserver.loginRequest": {
"type": "object",
"properties": {
Expand Down
37 changes: 33 additions & 4 deletions docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -2674,10 +2674,7 @@
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/bookmarks.BookmarkDTO"
}
"$ref": "#/definitions/httpserver.listBookmarksResponse"
}
}
}
Expand Down Expand Up @@ -4543,9 +4540,18 @@
"author": {
"type": "string"
},
"cover": {
"type": "string"
},
"description": {
"type": "string"
},
"domain": {
"type": "string"
},
"favicon": {
"type": "string"
},
"highlights": {
"type": "array",
"items": {
Expand All @@ -4558,6 +4564,9 @@
"published_at": {
"type": "string"
},
"site_name": {
"type": "string"
},
"tags": {
"type": "array",
"items": {
Expand Down Expand Up @@ -4691,6 +4700,26 @@
}
}
},
"httpserver.listBookmarksResponse": {
"type": "object",
"properties": {
"bookmarks": {
"type": "array",
"items": {
"$ref": "#/definitions/bookmarks.BookmarkDTO"
}
},
"limit": {
"type": "integer"
},
"offset": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
},
"httpserver.loginRequest": {
"type": "object",
"properties": {
Expand Down
25 changes: 22 additions & 3 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,14 @@ definitions:
properties:
author:
type: string
cover:
type: string
description:
type: string
domain:
type: string
favicon:
type: string
highlights:
items:
$ref: '#/definitions/bookmarks.Highlight'
Expand All @@ -223,6 +229,8 @@ definitions:
type: string
published_at:
type: string
site_name:
type: string
tags:
items:
type: string
Expand Down Expand Up @@ -314,6 +322,19 @@ definitions:
public_url:
type: string
type: object
httpserver.listBookmarksResponse:
properties:
bookmarks:
items:
$ref: '#/definitions/bookmarks.BookmarkDTO'
type: array
limit:
type: integer
offset:
type: integer
total:
type: integer
type: object
httpserver.loginRequest:
properties:
email:
Expand Down Expand Up @@ -2026,9 +2047,7 @@ paths:
- $ref: '#/definitions/httpserver.JSONResult'
- properties:
data:
items:
$ref: '#/definitions/bookmarks.BookmarkDTO'
type: array
$ref: '#/definitions/httpserver.listBookmarksResponse'
type: object
"401":
description: Unauthorized
Expand Down
2 changes: 1 addition & 1 deletion internal/core/bookmarks/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ type DAO interface {
DeleteBookmarksByUser(ctx context.Context, db db.DBTX, userID pgtype.UUID) error
GetBookmarkByUUID(ctx context.Context, db db.DBTX, argUuid uuid.UUID) (db.Bookmark, error)
GetBookmarkByURL(ctx context.Context, db db.DBTX, arg db.GetBookmarkByURLParams) (db.Bookmark, error)
ListBookmarks(ctx context.Context, db db.DBTX, arg db.ListBookmarksParams) ([]db.Bookmark, error)
ListBookmarks(ctx context.Context, db db.DBTX, arg db.ListBookmarksParams) ([]db.ListBookmarksRow, error)
UpdateBookmark(ctx context.Context, db db.DBTX, arg db.UpdateBookmarkParams) (db.Bookmark, error)
}
27 changes: 27 additions & 0 deletions internal/core/bookmarks/dto.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,33 @@ func (b *BookmarkDTO) Load(dbo *db.Bookmark) {
}
}

// Load converts a database object to a domain object
func (b *BookmarkDTO) LoadWithCount(dbo *db.ListBookmarksRow) {
b.ID = dbo.Uuid
b.UserID = dbo.UserID.Bytes
b.URL = dbo.Url
b.Title = dbo.Title.String
b.Summary = dbo.Summary.String
if dbo.SummaryEmbeddings != nil {
b.SummaryEmbedding = dbo.SummaryEmbeddings.Slice()
}
b.Content = dbo.Content.String
if dbo.ContentEmbeddings != nil {
b.ContentEmbedding = dbo.ContentEmbeddings.Slice()
}
b.HTML = dbo.Html.String
b.Screenshot = dbo.Screenshot.String
b.CreatedAt = dbo.CreatedAt.Time
b.UpdatedAt = dbo.UpdatedAt.Time

if dbo.Metadata != nil {
if err := json.Unmarshal(dbo.Metadata, &b.Metadata); err != nil {
logger.Default.Warn("failed to unmarshal Bookmark metadata",
"err", err, "metadata", string(dbo.Metadata))
}
}
}

// Dump converts a domain object to a database object for creation
func (b *BookmarkDTO) Dump() db.CreateBookmarkParams {
metadata, _ := json.Marshal(b.Metadata)
Expand Down
8 changes: 5 additions & 3 deletions internal/core/bookmarks/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,15 @@ func (s *Service) Get(ctx context.Context, tx db.DBTX, id, userID uuid.UUID) (*B
}

// ListBookmarks retrieves a paginated list of bookmarks for a user
func (s *Service) List(ctx context.Context, tx db.DBTX, userID uuid.UUID, limit, offset int32) ([]*BookmarkDTO, error) {
func (s *Service) List(ctx context.Context, tx db.DBTX, userID uuid.UUID, limit, offset int32) ([]*BookmarkDTO, int64, error) {
if limit <= 0 || limit > 100 {
limit = 50 // Default limit
}
if offset < 0 {
offset = 0
}

totalCount := int64(0)
bookmarks, err := s.dao.ListBookmarks(ctx, tx, db.ListBookmarksParams{
UserID: pgtype.UUID{Bytes: userID, Valid: true},
Limit: limit,
Expand All @@ -95,13 +96,14 @@ func (s *Service) List(ctx context.Context, tx db.DBTX, userID uuid.UUID, limit,
dtos := make([]*BookmarkDTO, 0, len(bookmarks))
for _, bookmark := range bookmarks {
var dto BookmarkDTO
dto.Load(&bookmark)
dto.LoadWithCount(&bookmark)
dto.HTML = ""
dto.SummaryEmbedding = nil
dto.ContentEmbedding = nil
dtos = append(dtos, &dto)
totalCount = bookmark.TotalCount
}
return dtos, err
return dtos, totalCount, err
}

// UpdateBookmark updates an existing bookmark
Expand Down
37 changes: 31 additions & 6 deletions internal/pkg/db/bookmarks.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 4ffc3e9

Please sign in to comment.