Redmage/api/reddit/get_posts.go

121 lines
2.9 KiB
Go
Raw Normal View History

2024-04-10 17:13:07 +07:00
package reddit
import (
"context"
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"strconv"
2024-04-10 22:38:19 +07:00
"strings"
2024-04-10 17:13:07 +07:00
"github.com/tigorlazuardi/redmage/pkg/errs"
)
type SubredditType int
2024-04-26 10:51:09 +07:00
func (su *SubredditType) UnmarshalJSON(b []byte) error {
if len(b) == 4 && string(b) == "null" {
2024-04-26 10:51:09 +07:00
return nil
}
s, err := strconv.Unquote(string(b))
if err != nil {
return errs.Wrapw(err, "failed to unquote string json value").Code(http.StatusBadRequest)
}
return su.Parse(s)
}
func (su *SubredditType) Parse(s string) error {
switch s {
2024-04-26 10:51:09 +07:00
case `"user"`, `"u"`, "1":
*su = SubredditTypeUser
return nil
case `"r"`, `"subreddit"`, "0", "":
2024-04-26 10:51:09 +07:00
*su = SubredditTypeSub
return nil
}
return errs.
Fail("subreddit type not recognized. Valid values are '' (empty), 'user', 'u', 'r', 'subreddit', 0, 1, and null",
"got", s,
2024-04-26 10:51:09 +07:00
).
Code(http.StatusBadRequest)
}
2024-04-10 17:13:07 +07:00
const (
SubredditTypeSub SubredditType = iota
SubredditTypeUser
)
func (s SubredditType) Code() string {
switch s {
case SubredditTypeUser:
return "user"
default:
return "r"
}
}
2024-04-26 10:51:09 +07:00
func (s SubredditType) String() string {
2024-05-27 21:10:37 +07:00
switch s {
case SubredditTypeUser:
return "User"
default:
return "Subreddit"
}
2024-04-26 10:51:09 +07:00
}
2024-04-10 17:13:07 +07:00
type GetPostsParam struct {
Subreddit string
Limit int
2024-04-25 12:31:20 +07:00
After string
2024-04-10 17:13:07 +07:00
SubredditType SubredditType
}
2024-04-10 22:38:19 +07:00
func (reddit *Reddit) GetPosts(ctx context.Context, params GetPostsParam) (posts Listing, err error) {
2024-04-26 10:51:09 +07:00
ctx, span := tracer.Start(ctx, "*Reddit.GetPosts")
defer span.End()
2024-04-25 12:31:20 +07:00
url := fmt.Sprintf("https://reddit.com/%s/%s.json?limit=%d&after=%s", params.SubredditType.Code(), params.Subreddit, params.Limit, params.After)
2024-04-10 17:13:07 +07:00
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
if err != nil {
return posts, errs.Wrapw(err, "reddit: failed to create http request instance", "url", url, "params", params)
}
req.Header.Set("User-Agent", reddit.Config.String("download.useragent"))
2024-04-10 17:13:07 +07:00
res, err := reddit.Client.Do(req)
if err != nil {
return posts, errs.Wrapw(err, "reddit: failed to execute http request", "url", url, "params", params)
}
defer res.Body.Close()
2024-04-26 22:13:04 +07:00
if res.StatusCode == http.StatusTooManyRequests {
return posts, errs.Fail("reddit: too many requests",
"retry_after", res.Header.Get("Retry-After"),
"url", url,
)
2024-04-26 22:13:04 +07:00
}
2024-04-10 17:13:07 +07:00
if res.StatusCode != http.StatusOK {
body, _ := io.ReadAll(res.Body)
return posts, errs.Fail("reddit: unexpected status code when executing GetPosts",
slog.Group("request", "url", url, "params", params),
2024-04-10 22:38:19 +07:00
slog.Group("response", "status_code", res.StatusCode, "body", formatLogBody(res, body)),
2024-04-14 00:32:55 +07:00
).Code(res.StatusCode)
2024-04-10 17:13:07 +07:00
}
err = json.NewDecoder(res.Body).Decode(&posts)
if err != nil {
return posts, errs.Wrapw(err, "reddit: failed to parse response body when getting posts from reddit", "url", url, "params", params)
}
return posts, nil
}
2024-04-10 22:38:19 +07:00
func formatLogBody(res *http.Response, body []byte) any {
if strings.HasPrefix(res.Header.Get("Content-Type"), "application/json") {
return json.RawMessage(body)
}
return string(body)
}