2024-04-08 23:18:09 +07:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-05-01 18:16:54 +07:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2024-04-08 23:18:09 +07:00
|
|
|
|
2024-04-24 13:01:13 +07:00
|
|
|
"github.com/stephenafamo/bob"
|
2024-05-02 15:42:10 +07:00
|
|
|
"github.com/stephenafamo/bob/dialect/sqlite"
|
2024-04-24 13:01:13 +07:00
|
|
|
"github.com/stephenafamo/bob/dialect/sqlite/dialect"
|
|
|
|
"github.com/stephenafamo/bob/dialect/sqlite/sm"
|
|
|
|
"github.com/tigorlazuardi/redmage/models"
|
2024-04-08 23:18:09 +07:00
|
|
|
"github.com/tigorlazuardi/redmage/pkg/errs"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ListSubredditsParams struct {
|
2024-05-01 18:16:54 +07:00
|
|
|
Q string
|
|
|
|
All bool
|
|
|
|
Limit int64
|
|
|
|
Offset int64
|
|
|
|
OrderBy string
|
|
|
|
Sort string
|
2024-05-28 16:39:38 +07:00
|
|
|
NSFW int
|
2024-05-01 18:16:54 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *ListSubredditsParams) FillFromQuery(q Queryable) {
|
|
|
|
l.Q = q.Get("q")
|
|
|
|
l.All, _ = strconv.ParseBool(q.Get("all"))
|
|
|
|
l.Limit, _ = strconv.ParseInt(q.Get("limit"), 10, 64)
|
|
|
|
if l.Limit < 0 {
|
|
|
|
l.Limit = 25
|
|
|
|
} else if l.Limit > 100 {
|
|
|
|
l.Limit = 100
|
|
|
|
}
|
|
|
|
l.Offset, _ = strconv.ParseInt(q.Get("offset"), 10, 64)
|
|
|
|
if l.Offset < 0 {
|
|
|
|
l.Offset = 0
|
|
|
|
}
|
|
|
|
l.OrderBy = q.Get("order_by")
|
|
|
|
l.Sort = strings.ToLower(q.Get("sort"))
|
2024-05-28 16:39:38 +07:00
|
|
|
if nsfw, err := strconv.Atoi(q.Get("nsfw")); err == nil {
|
|
|
|
l.NSFW = nsfw
|
|
|
|
} else {
|
|
|
|
l.NSFW = -1
|
|
|
|
}
|
2024-04-24 13:01:13 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l ListSubredditsParams) Query() (expr []bob.Mod[*dialect.SelectQuery]) {
|
2024-05-01 18:16:54 +07:00
|
|
|
if l.Q != "" {
|
|
|
|
expr = append(expr, models.SelectWhere.Subreddits.Name.Like("%"+l.Q+"%"))
|
2024-04-24 13:01:13 +07:00
|
|
|
}
|
|
|
|
if l.Limit > 0 {
|
|
|
|
expr = append(expr, sm.Limit(l.Limit))
|
|
|
|
}
|
|
|
|
if l.Offset > 0 {
|
|
|
|
expr = append(expr, sm.Offset(l.Offset))
|
|
|
|
}
|
|
|
|
if l.OrderBy != "" {
|
|
|
|
if l.Sort == "desc" {
|
2024-05-02 15:42:10 +07:00
|
|
|
expr = append(expr, sm.OrderBy(sqlite.Quote(l.OrderBy)).Desc())
|
2024-04-24 13:01:13 +07:00
|
|
|
} else {
|
2024-05-02 15:42:10 +07:00
|
|
|
expr = append(expr, sm.OrderBy(sqlite.Quote(l.OrderBy)).Asc())
|
2024-04-24 13:01:13 +07:00
|
|
|
}
|
2024-05-01 18:16:54 +07:00
|
|
|
} else {
|
2024-05-28 16:39:38 +07:00
|
|
|
expr = append(expr, sm.OrderBy(models.SubredditColumns.UpdatedAt).Desc())
|
2024-04-24 13:01:13 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return expr
|
|
|
|
}
|
|
|
|
|
2024-05-28 16:39:38 +07:00
|
|
|
func (l ListSubredditsParams) ImageCoverQuery(subname string) (expr []bob.Mod[*dialect.SelectQuery]) {
|
|
|
|
expr = make([]bob.Mod[*dialect.SelectQuery], 0, 4)
|
|
|
|
expr = append(expr, models.SelectWhere.Images.Subreddit.EQ(subname))
|
|
|
|
if l.NSFW >= 0 {
|
|
|
|
expr = append(expr, models.SelectWhere.Images.NSFW.EQ(int32(l.NSFW)))
|
|
|
|
}
|
|
|
|
expr = append(expr, sm.Limit(1))
|
|
|
|
expr = append(expr, sm.OrderBy(models.ImageColumns.CreatedAt).Desc())
|
|
|
|
return expr
|
|
|
|
}
|
|
|
|
|
2024-04-24 13:01:13 +07:00
|
|
|
func (l ListSubredditsParams) CountQuery() (expr []bob.Mod[*dialect.SelectQuery]) {
|
2024-05-01 18:16:54 +07:00
|
|
|
if l.Q != "" {
|
|
|
|
expr = append(expr, models.SelectWhere.Subreddits.Name.Like("%"+l.Q+"%"))
|
2024-04-24 13:01:13 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return expr
|
2024-04-08 23:18:09 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
type ListSubredditsResult struct {
|
|
|
|
Total int64
|
2024-04-24 13:01:13 +07:00
|
|
|
Data models.SubredditSlice
|
2024-04-08 23:18:09 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (api *API) ListSubreddits(ctx context.Context, arg ListSubredditsParams) (result ListSubredditsResult, err error) {
|
2024-04-13 00:15:31 +07:00
|
|
|
ctx, span := tracer.Start(ctx, "api.ListSubreddits")
|
|
|
|
defer span.End()
|
2024-04-08 23:18:09 +07:00
|
|
|
|
2024-04-25 13:28:35 +07:00
|
|
|
result.Data, err = models.Subreddits.Query(ctx, api.db, arg.Query()...).All()
|
2024-04-08 23:18:09 +07:00
|
|
|
if err != nil {
|
2024-04-24 13:01:13 +07:00
|
|
|
return result, errs.Wrapw(err, "failed to list subreddits", "query", arg)
|
2024-04-08 23:18:09 +07:00
|
|
|
}
|
2024-04-24 13:01:13 +07:00
|
|
|
|
2024-04-25 13:28:35 +07:00
|
|
|
result.Total, err = models.Subreddits.Query(ctx, api.db, arg.CountQuery()...).Count()
|
2024-04-08 23:18:09 +07:00
|
|
|
if err != nil {
|
2024-04-24 13:01:13 +07:00
|
|
|
return result, errs.Wrapw(err, "failed to count subreddits", "query", arg)
|
2024-04-08 23:18:09 +07:00
|
|
|
}
|
2024-04-24 13:01:13 +07:00
|
|
|
|
|
|
|
return result, nil
|
2024-04-08 23:18:09 +07:00
|
|
|
}
|
2024-05-01 18:16:54 +07:00
|
|
|
|
|
|
|
// ListSubredditsWithCover returns list of subreddits with cover image.
|
|
|
|
//
|
|
|
|
// Image Relationship `R` struct will be nil if there is no cover image.
|
|
|
|
func (api *API) ListSubredditsWithCover(ctx context.Context, arg ListSubredditsParams) (result ListSubredditsResult, err error) {
|
|
|
|
ctx, span := tracer.Start(ctx, "api.ListSubredditsWithCover")
|
|
|
|
defer span.End()
|
|
|
|
|
|
|
|
result, err = api.ListSubreddits(ctx, arg)
|
|
|
|
if err != nil {
|
|
|
|
return result, errs.Wrapw(err, "failed to list subreddits with cover")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cannot do batch query because we cannot use GROUP BY with ORDER BY in consistent manner.
|
|
|
|
//
|
|
|
|
// The problem gets worse when using custom ORDER BY from client.
|
|
|
|
//
|
|
|
|
// For consistency, we query images one by one.
|
|
|
|
//
|
|
|
|
// Subreddit list is expected to be small, so this should be fine since SQLITE has no network latency.
|
|
|
|
for _, subreddit := range result.Data {
|
2024-05-28 16:39:38 +07:00
|
|
|
subreddit.R.Images, err = models.Images.Query(ctx, api.db, arg.ImageCoverQuery(subreddit.Name)...).All()
|
2024-05-01 18:16:54 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|