Add stylesheet methods and more to subreddit service

Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
Vartan Benohanian 2020-09-13 21:43:59 -04:00
parent 9c85166c66
commit 7bb73ddc6e
4 changed files with 308 additions and 0 deletions

View file

@ -1,6 +1,7 @@
package reddit package reddit
import ( import (
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"errors" "errors"
@ -113,6 +114,20 @@ type SubredditTrafficStats struct {
Subscribers int `json:"subscribers"` Subscribers int `json:"subscribers"`
} }
// SubredditImage is an image part of the image set of a subreddit.
type SubredditImage struct {
Name string `json:"name"`
Link string `json:"link"`
URL string `json:"url"`
}
// SubredditStyleSheet contains the subreddit's styling information.
type SubredditStyleSheet struct {
SubredditID string `json:"subreddit_id"`
Images []*SubredditImage `json:"images"`
StyleSheet string `json:"stylesheet"`
}
// UnmarshalJSON implements the json.Unmarshaler interface. // UnmarshalJSON implements the json.Unmarshaler interface.
func (s *SubredditTrafficStats) UnmarshalJSON(b []byte) error { func (s *SubredditTrafficStats) UnmarshalJSON(b []byte) error {
var data [4]int var data [4]int
@ -816,3 +831,126 @@ func (s *SubredditService) Traffic(ctx context.Context, subreddit string) ([]*Su
return root.Day, root.Hour, root.Month, resp, nil return root.Day, root.Hour, root.Month, resp, nil
} }
// StyleSheet returns the subreddit's style sheet, as well as some information about images.
func (s *SubredditService) StyleSheet(ctx context.Context, subreddit string) (*SubredditStyleSheet, *Response, error) {
path := fmt.Sprintf("r/%s/about/stylesheet", subreddit)
req, err := s.client.NewRequest(http.MethodGet, path, nil)
if err != nil {
return nil, nil, err
}
root := new(thing)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
styleSheet, _ := root.StyleSheet()
return styleSheet, resp, nil
}
// StyleSheetRaw returns the subreddit's style sheet with all comments and newlines stripped.
func (s *SubredditService) StyleSheetRaw(ctx context.Context, subreddit string) (string, *Response, error) {
path := fmt.Sprintf("r/%s/stylesheet", subreddit)
req, err := s.client.NewRequest(http.MethodGet, path, nil)
if err != nil {
return "", nil, err
}
buf := new(bytes.Buffer)
resp, err := s.client.Do(ctx, req, buf)
if err != nil {
return "", resp, err
}
return buf.String(), resp, nil
}
// UpdateStyleSheet updates the style sheet of the subreddit.
// Providing a reason is optional.
func (s *SubredditService) UpdateStyleSheet(ctx context.Context, subreddit, styleSheet, reason string) (*Response, error) {
path := fmt.Sprintf("r/%s/api/subreddit_stylesheet", subreddit)
form := url.Values{}
form.Set("api_type", "json")
form.Set("op", "save")
form.Set("stylesheet_contents", styleSheet)
if reason != "" {
form.Set("reason", reason)
}
req, err := s.client.NewRequest(http.MethodPost, path, form)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// RemoveHeaderImage removes the subreddit's custom header image.
// The call succeeds even if there's no header image.
func (s *SubredditService) RemoveHeaderImage(ctx context.Context, subreddit string) (*Response, error) {
path := fmt.Sprintf("r/%s/api/delete_sr_header", subreddit)
form := url.Values{}
form.Set("api_type", "json")
req, err := s.client.NewRequest(http.MethodPost, path, form)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// RemoveMobileIcon removes the subreddit's custom mobile icon.
// The call succeeds even if there's no mobile icon.
func (s *SubredditService) RemoveMobileIcon(ctx context.Context, subreddit string) (*Response, error) {
path := fmt.Sprintf("r/%s/api/delete_sr_icon", subreddit)
form := url.Values{}
form.Set("api_type", "json")
req, err := s.client.NewRequest(http.MethodPost, path, form)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// RemoveMobileBanner removes the subreddit's custom mobile banner.
// The call succeeds even if there's no mobile banner.
func (s *SubredditService) RemoveMobileBanner(ctx context.Context, subreddit string) (*Response, error) {
path := fmt.Sprintf("r/%s/api/delete_sr_banner", subreddit)
form := url.Values{}
form.Set("api_type", "json")
req, err := s.client.NewRequest(http.MethodPost, path, form)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// RemoveImage removes an image from the subreddit's custom image set.
// The call succeeds even if the named image does not exist.
func (s *SubredditService) RemoveImage(ctx context.Context, subreddit, imageName string) (*Response, error) {
path := fmt.Sprintf("r/%s/api/delete_sr_img", subreddit)
form := url.Values{}
form.Set("api_type", "json")
form.Set("img_name", imageName)
req, err := s.client.NewRequest(http.MethodPost, path, form)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View file

@ -316,6 +316,24 @@ var expectedMonthTraffic = []*SubredditTrafficStats{
{&Timestamp{time.Date(2020, 7, 1, 0, 0, 0, 0, time.UTC)}, 4, 264, 0}, {&Timestamp{time.Date(2020, 7, 1, 0, 0, 0, 0, time.UTC)}, 4, 264, 0},
} }
var expectedStyleSheet = &SubredditStyleSheet{
SubredditID: "t5_2rc7j",
Images: []*SubredditImage{
{
Name: "gopher",
Link: "url(%%gopher%%)",
URL: "http://b.thumbs.redditmedia.com/q5Wb6hTPm2Bd6Of9_xMrTu4n5qgAljJNqtnbE3Tging.png",
},
},
StyleSheet: `.flair-gopher {
background: url(%%gopher%%) no-repeat;
border: 0;
padding: 0;
width: 16px;
height: 16px;
}`,
}
func TestSubredditService_HotPosts(t *testing.T) { func TestSubredditService_HotPosts(t *testing.T) {
client, mux, teardown := setup() client, mux, teardown := setup()
defer teardown() defer teardown()
@ -1155,3 +1173,133 @@ func TestSubredditService_Traffic(t *testing.T) {
require.Equal(t, expectedHourTraffic, hourTraffic) require.Equal(t, expectedHourTraffic, hourTraffic)
require.Equal(t, expectedMonthTraffic, monthTraffic) require.Equal(t, expectedMonthTraffic, monthTraffic)
} }
func TestSubredditService_StyleSheet(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
blob, err := readFileContents("../testdata/subreddit/stylesheet.json")
require.NoError(t, err)
mux.HandleFunc("/r/testsubreddit/about/stylesheet", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodGet, r.Method)
fmt.Fprint(w, blob)
})
styleSheet, _, err := client.Subreddit.StyleSheet(ctx, "testsubreddit")
require.NoError(t, err)
require.Equal(t, expectedStyleSheet, styleSheet)
}
func TestSubredditService_StyleSheetRaw(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
mux.HandleFunc("/r/testsubreddit/stylesheet", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodGet, r.Method)
fmt.Fprint(w, "* { box-sizing: border-box; }")
})
styleSheet, _, err := client.Subreddit.StyleSheetRaw(ctx, "testsubreddit")
require.NoError(t, err)
require.Equal(t, "* { box-sizing: border-box; }", styleSheet)
}
func TestSubredditService_UpdateStyleSheet(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
mux.HandleFunc("/r/testsubreddit/api/subreddit_stylesheet", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
form := url.Values{}
form.Set("api_type", "json")
form.Set("op", "save")
form.Set("stylesheet_contents", "* { box-sizing: border-box; }")
form.Set("reason", "testreason")
err := r.ParseForm()
require.NoError(t, err)
require.Equal(t, form, r.PostForm)
})
_, err := client.Subreddit.UpdateStyleSheet(ctx, "testsubreddit", "* { box-sizing: border-box; }", "testreason")
require.NoError(t, err)
}
func TestSubredditService_RemoveHeaderImage(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
mux.HandleFunc("/r/testsubreddit/api/delete_sr_header", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
form := url.Values{}
form.Set("api_type", "json")
err := r.ParseForm()
require.NoError(t, err)
require.Equal(t, form, r.PostForm)
})
_, err := client.Subreddit.RemoveHeaderImage(ctx, "testsubreddit")
require.NoError(t, err)
}
func TestSubredditService_RemoveMobileIcon(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
mux.HandleFunc("/r/testsubreddit/api/delete_sr_icon", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
form := url.Values{}
form.Set("api_type", "json")
err := r.ParseForm()
require.NoError(t, err)
require.Equal(t, form, r.PostForm)
})
_, err := client.Subreddit.RemoveMobileIcon(ctx, "testsubreddit")
require.NoError(t, err)
}
func TestSubredditService_RemoveMobileBanner(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
mux.HandleFunc("/r/testsubreddit/api/delete_sr_banner", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
form := url.Values{}
form.Set("api_type", "json")
err := r.ParseForm()
require.NoError(t, err)
require.Equal(t, form, r.PostForm)
})
_, err := client.Subreddit.RemoveMobileBanner(ctx, "testsubreddit")
require.NoError(t, err)
}
func TestSubredditService_RemoveImage(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
mux.HandleFunc("/r/testsubreddit/api/delete_sr_img", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
form := url.Values{}
form.Set("api_type", "json")
form.Set("img_name", "testimage")
err := r.ParseForm()
require.NoError(t, err)
require.Equal(t, form, r.PostForm)
})
_, err := client.Subreddit.RemoveImage(ctx, "testsubreddit", "testimage")
require.NoError(t, err)
}

View file

@ -23,6 +23,7 @@ const (
kindWikiPage = "wikipage" kindWikiPage = "wikipage"
kindWikiPageListing = "wikipagelisting" kindWikiPageListing = "wikipagelisting"
kindWikiPageSettings = "wikipagesettings" kindWikiPageSettings = "wikipagesettings"
kindStyleSheet = "stylesheet"
) )
type anchor interface { type anchor interface {
@ -106,6 +107,8 @@ func (t *thing) UnmarshalJSON(b []byte) error {
v = new([]string) v = new([]string)
case kindWikiPageSettings: case kindWikiPageSettings:
v = new(WikiPageSettings) v = new(WikiPageSettings)
case kindStyleSheet:
v = new(SubredditStyleSheet)
default: default:
return fmt.Errorf("unrecognized kind: %q", t.Kind) return fmt.Errorf("unrecognized kind: %q", t.Kind)
} }
@ -206,6 +209,11 @@ func (t *thing) WikiPageSettings() (v *WikiPageSettings, ok bool) {
return return
} }
func (t *thing) StyleSheet() (v *SubredditStyleSheet, ok bool) {
v, ok = t.Data.(*SubredditStyleSheet)
return
}
// listing is a list of things coming from the Reddit API. // listing is a list of things coming from the Reddit API.
// It also contains the after/before anchors useful for subsequent requests. // It also contains the after/before anchors useful for subsequent requests.
type listing struct { type listing struct {

14
testdata/subreddit/stylesheet.json vendored Normal file
View file

@ -0,0 +1,14 @@
{
"kind": "stylesheet",
"data": {
"images": [
{
"url": "http://b.thumbs.redditmedia.com/q5Wb6hTPm2Bd6Of9_xMrTu4n5qgAljJNqtnbE3Tging.png",
"link": "url(%%gopher%%)",
"name": "gopher"
}
],
"subreddit_id": "t5_2rc7j",
"stylesheet": ".flair-gopher {\n background: url(%%gopher%%) no-repeat;\n border: 0;\n padding: 0;\n width: 16px;\n height: 16px;\n}"
}
}