Add stylesheet methods and more to subreddit service
Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
parent
9c85166c66
commit
7bb73ddc6e
4 changed files with 308 additions and 0 deletions
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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
14
testdata/subreddit/stylesheet.json
vendored
Normal 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}"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue