Added pagination to invites #34
@@ -3,6 +3,7 @@ package handler
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/robindittmar/dttmr-api/internal/api/response"
|
||||
"github.com/robindittmar/dttmr-api/internal/domain"
|
||||
@@ -94,12 +95,59 @@ func (h *InviteHandler) DeleteInvite(w http.ResponseWriter, r *http.Request) {
|
||||
// @Tags Invite
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} []domain.Invite
|
||||
// @Param page query int false "page"
|
||||
// @Param count query int false "count"
|
||||
// @Success 200 {object} response.Paginated[domain.Invite]
|
||||
// @Error 400 {object} response.ErrorResponse "failed to decode request body"
|
||||
// @Error 400 {object} response.ErrorResponse "invalid value for page"
|
||||
// @Error 400 {object} response.ErrorResponse "invalid value for count"
|
||||
// @Error 500 {object} response.ErrorResponse "failed to get invites"
|
||||
// @Router /user/invites [get]
|
||||
func (h *InviteHandler) GetInvites(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
pageStr := r.URL.Query().Get("page")
|
||||
if pageStr == "" {
|
||||
pageStr = "1"
|
||||
}
|
||||
countStr := r.URL.Query().Get("count")
|
||||
if countStr == "" {
|
||||
countStr = "10"
|
||||
}
|
||||
|
||||
page, err := strconv.Atoi(pageStr)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx,
|
||||
"failed to read page from query",
|
||||
slog.String("page", pageStr))
|
||||
response.Error(ctx, w, http.StatusBadRequest, "failed to decode request url")
|
||||
return
|
||||
}
|
||||
|
||||
count, err := strconv.Atoi(countStr)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx,
|
||||
"failed to read count from query",
|
||||
slog.String("count", countStr))
|
||||
response.Error(ctx, w, http.StatusBadRequest, "failed to decode request url")
|
||||
return
|
||||
}
|
||||
|
||||
if page < 1 {
|
||||
slog.ErrorContext(ctx,
|
||||
"page parameter is invalid",
|
||||
slog.Int("page", page))
|
||||
response.Error(ctx, w, http.StatusBadRequest, "invalid value for page")
|
||||
return
|
||||
}
|
||||
if count <= 0 {
|
||||
slog.ErrorContext(ctx,
|
||||
"count parameter is invalid",
|
||||
slog.Int("count", count))
|
||||
response.Error(ctx, w, http.StatusBadRequest, "invalid value for count")
|
||||
return
|
||||
}
|
||||
|
||||
authContext, err := domain.GetAuthContext(ctx)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to get auth context", slog.Any("error", err))
|
||||
@@ -107,12 +155,23 @@ func (h *InviteHandler) GetInvites(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
invites, err := h.InviteService.GetInvites(ctx, authContext.UserID)
|
||||
invites, err := h.InviteService.GetInvites(ctx, authContext.UserID, page, count)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to get invites", slog.Any("error", err))
|
||||
response.Error(ctx, w, http.StatusInternalServerError, "failed to get invites")
|
||||
return
|
||||
}
|
||||
|
||||
response.JSON(ctx, w, http.StatusOK, invites)
|
||||
total, err := h.InviteService.CountInvites(ctx, authContext.UserID)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "failed to count invites", slog.Any("error", err))
|
||||
response.Error(ctx, w, http.StatusInternalServerError, "failed to get invites")
|
||||
return
|
||||
}
|
||||
|
||||
response.JSON(ctx, w, http.StatusOK, response.Paginated[domain.Invite]{
|
||||
Count: len(invites),
|
||||
Total: total,
|
||||
Data: invites,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package request
|
||||
|
||||
type GetInvitesPayload struct {
|
||||
Page int
|
||||
CountPerPage int
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package response
|
||||
|
||||
type Paginated[T any] struct {
|
||||
Count int `json:"count"`
|
||||
Total int `json:"total"`
|
||||
Data []T `json:"data"`
|
||||
}
|
||||
@@ -25,7 +25,8 @@ type InviteRepository interface {
|
||||
DeleteInvite(ctx context.Context, userID string, inviteID string) error
|
||||
ConsumeInvite(ctx context.Context, inviteID string, inviteeUserID string) error
|
||||
GetInvite(ctx context.Context, code string) (*Invite, error)
|
||||
GetInvites(ctx context.Context, userID string) ([]Invite, error)
|
||||
GetInvites(ctx context.Context, userID string, offset int, count int) ([]Invite, error)
|
||||
CountInvites(ctx context.Context, userID string) (int, error)
|
||||
}
|
||||
|
||||
type InviteService struct {
|
||||
@@ -89,10 +90,19 @@ func (s *InviteService) GetInvite(ctx context.Context, code string) (*Invite, er
|
||||
return invite, nil
|
||||
}
|
||||
|
||||
func (s *InviteService) GetInvites(ctx context.Context, userID string) ([]Invite, error) {
|
||||
func (s *InviteService) GetInvites(ctx context.Context, userID string, page int, countPerPage int) ([]Invite, error) {
|
||||
if userID == "" {
|
||||
return nil, ErrUserIDMissing
|
||||
}
|
||||
|
||||
return s.repo.GetInvites(ctx, userID)
|
||||
offset := (page - 1) * countPerPage
|
||||
return s.repo.GetInvites(ctx, userID, offset, countPerPage)
|
||||
}
|
||||
|
||||
func (s *InviteService) CountInvites(ctx context.Context, userID string) (int, error) {
|
||||
if userID == "" {
|
||||
return 0, ErrUserIDMissing
|
||||
}
|
||||
|
||||
return s.repo.CountInvites(ctx, userID)
|
||||
}
|
||||
|
||||
@@ -81,10 +81,10 @@ func (r *InviteRepo) GetInvite(ctx context.Context, code string) (*domain.Invite
|
||||
return &invite, nil
|
||||
}
|
||||
|
||||
func (r *InviteRepo) GetInvites(ctx context.Context, userID string) ([]domain.Invite, error) {
|
||||
func (r *InviteRepo) GetInvites(ctx context.Context, userID string, offset int, count int) ([]domain.Invite, error) {
|
||||
rows, err := r.db.QueryContext(ctx,
|
||||
"SELECT id, code, expires_at, consumed_at FROM invites WHERE inviter_user_id=$1",
|
||||
userID,
|
||||
"SELECT id, code, expires_at, consumed_at FROM invites WHERE inviter_user_id=$1 ORDER BY created_at DESC OFFSET $2 LIMIT $3",
|
||||
userID, offset, count,
|
||||
)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
@@ -107,3 +107,16 @@ func (r *InviteRepo) GetInvites(ctx context.Context, userID string) ([]domain.In
|
||||
|
||||
return invites, nil
|
||||
}
|
||||
|
||||
func (r *InviteRepo) CountInvites(ctx context.Context, userID string) (int, error) {
|
||||
var count int
|
||||
err := r.db.QueryRowContext(ctx,
|
||||
"SELECT COUNT(*) FROM invites WHERE inviter_user_id=$1",
|
||||
userID,
|
||||
).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to count invites: %w", err)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user