feat: articles pagination
This commit is contained in:
1 parent
a2d224240c
commit
73f4b539fe
45 files changed
+3528
-317
No files matched your search
@@ -11,7 +11,9 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -268,26 +270,89 @@ func (h *Handler) callerIsVerified(r *http.Request) bool {
|
||||
return currentUser.IsVerified
|
||||
}
|
||||
|
||||
const (
|
||||
defaultListPage = 1
|
||||
defaultListSize = 25
|
||||
)
|
||||
|
||||
type listQuery struct {
|
||||
Page int `validate:"gte=1"`
|
||||
Size int `validate:"oneof=5 25 50 100"`
|
||||
Q string `validate:"max=200"`
|
||||
Status string `validate:"omitempty,oneof=online draft"`
|
||||
Sort string `validate:"oneof=title:asc title:desc status:asc status:desc created_at:asc created_at:desc updated_at:asc updated_at:desc"`
|
||||
}
|
||||
|
||||
const defaultListSort = "updated_at:desc"
|
||||
|
||||
func parseSort(value string) domain.Sort {
|
||||
field, direction, _ := strings.Cut(value, ":")
|
||||
return domain.Sort{Field: domain.SortField(field), Direction: domain.SortDirection(direction)}
|
||||
}
|
||||
|
||||
type listResponse struct {
|
||||
Items []articleResponse `json:"items"`
|
||||
Total int `json:"total"`
|
||||
Page int `json:"page"`
|
||||
Size int `json:"size"`
|
||||
Counts domain.Counts `json:"counts"`
|
||||
}
|
||||
|
||||
func intQueryParam(values url.Values, name string, fallback int) (int, error) {
|
||||
if !values.Has(name) {
|
||||
return fallback, nil
|
||||
}
|
||||
return strconv.Atoi(values.Get(name))
|
||||
}
|
||||
|
||||
func readListQuery(values url.Values) (listQuery, error) {
|
||||
page, err := intQueryParam(values, "page", defaultListPage)
|
||||
if err != nil {
|
||||
return listQuery{}, err
|
||||
}
|
||||
size, err := intQueryParam(values, "size", defaultListSize)
|
||||
if err != nil {
|
||||
return listQuery{}, err
|
||||
}
|
||||
status := values.Get("status")
|
||||
if values.Has("status") && status == "" {
|
||||
return listQuery{}, errors.New("empty status")
|
||||
}
|
||||
sortValue := defaultListSort
|
||||
if values.Has("sort") {
|
||||
sortValue = values.Get("sort")
|
||||
}
|
||||
return listQuery{Page: page, Size: size, Q: values.Get("q"), Status: status, Sort: sortValue}, nil
|
||||
}
|
||||
|
||||
func (h *Handler) ListArticles(w http.ResponseWriter, r *http.Request) {
|
||||
format, ok := parseBodyFormat(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var articles []domain.Article
|
||||
var err error
|
||||
if h.callerIsVerified(r) {
|
||||
articles, err = h.ListUseCase.ListAllArticles(format)
|
||||
} else {
|
||||
articles, err = h.ListUseCase.ListOnlineArticles(format)
|
||||
query, err := readListQuery(r.URL.Query())
|
||||
if err != nil || validator.Validate.Struct(query) != nil {
|
||||
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
page, err := h.ListUseCase.ListArticlesPage(useCases.ListArticlesCommand{
|
||||
Format: format,
|
||||
Verified: h.callerIsVerified(r),
|
||||
Page: query.Page,
|
||||
Size: query.Size,
|
||||
Q: query.Q,
|
||||
Status: query.Status,
|
||||
Sort: parseSort(query.Sort),
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to retrieve articles", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
responses := make([]articleResponse, 0, len(articles))
|
||||
for _, article := range articles {
|
||||
responses := make([]articleResponse, 0, len(page.Items))
|
||||
for _, article := range page.Items {
|
||||
response, err := toArticleResponse(article, format)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
|
||||
@@ -296,7 +361,7 @@ func (h *Handler) ListArticles(w http.ResponseWriter, r *http.Request) {
|
||||
responses = append(responses, response)
|
||||
}
|
||||
|
||||
articlesJson, err := json.Marshal(responses)
|
||||
articlesJson, err := json.Marshal(listResponse{Items: responses, Total: page.Total, Page: page.Page, Size: page.Size, Counts: page.Counts})
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
|
||||
return
|
||||
|
||||
Reference in new issue
Block a user