Create WikiService

Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
Vartan Benohanian
2020-09-03 23:25:16 -04:00
parent ffcc906c07
commit 9d5132f15c
7 changed files with 523 additions and 34 deletions
-8
View File
@@ -81,16 +81,8 @@ type inboxThings struct {
Messages []*Message
}
// init initializes or clears the listing.
func (t *inboxThings) init() {
t.Comments = make([]*Message, 0)
t.Messages = make([]*Message, 0)
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (t *inboxThings) UnmarshalJSON(b []byte) error {
t.init()
var things []inboxThing
if err := json.Unmarshal(b, &things); err != nil {
return err
+2
View File
@@ -78,6 +78,7 @@ type Client struct {
Stream *StreamService
Subreddit *SubredditService
User *UserService
Wiki *WikiService
oauth2Transport *oauth2.Transport
@@ -107,6 +108,7 @@ func newClient() *Client {
client.Stream = &StreamService{client: client}
client.Subreddit = &SubredditService{client: client}
client.User = &UserService{client: client}
client.Wiki = &WikiService{client: client}
postAndCommentService := &postAndCommentService{client: client}
client.Comment = &CommentService{client: client, postAndCommentService: postAndCommentService}
+1
View File
@@ -71,6 +71,7 @@ func testClientServices(t *testing.T, c *Client) {
"Stream",
"Subreddit",
"User",
"Wiki",
}
cp := reflect.ValueOf(c)
+32 -26
View File
@@ -6,19 +6,21 @@ import (
)
const (
kindComment = "t1"
kindUser = "t2"
kindPost = "t3"
kindMessage = "t4"
kindSubreddit = "t5"
kindTrophy = "t6"
kindListing = "Listing"
kindKarmaList = "KarmaList"
kindTrophyList = "TrophyList"
kindUserList = "UserList"
kindMore = "more"
kindModAction = "modaction"
kindMulti = "LabeledMulti"
kindComment = "t1"
kindUser = "t2"
kindPost = "t3"
kindMessage = "t4"
kindSubreddit = "t5"
kindTrophy = "t6"
kindListing = "Listing"
kindKarmaList = "KarmaList"
kindTrophyList = "TrophyList"
kindUserList = "UserList"
kindMore = "more"
kindModAction = "modaction"
kindMulti = "LabeledMulti"
kindWikiPageListing = "wikipagelisting"
kindWikiPageSettings = "wikipagesettings"
)
type anchor interface {
@@ -94,6 +96,10 @@ func (t *thing) UnmarshalJSON(b []byte) error {
v = new(trophyList)
case kindKarmaList:
v = new([]*SubredditKarma)
case kindWikiPageListing:
v = new([]string)
case kindWikiPageSettings:
v = new(WikiPageSettings)
default:
return fmt.Errorf("unrecognized kind: %q", t.Kind)
}
@@ -168,6 +174,19 @@ func (t *thing) Karma() ([]*SubredditKarma, bool) {
return *v, ok
}
func (t *thing) WikiPages() ([]string, bool) {
v, ok := t.Data.(*[]string)
if !ok {
return nil, ok
}
return *v, ok
}
func (t *thing) WikiPageSettings() (v *WikiPageSettings, ok bool) {
v, ok = t.Data.(*WikiPageSettings)
return
}
// listing is a list of things coming from the Reddit API.
// It also contains the after/before anchors useful for subsequent requests.
type listing struct {
@@ -263,21 +282,8 @@ type things struct {
Multis []*Multi
}
// init initializes or clears the listing.
func (t *things) init() {
t.Comments = make([]*Comment, 0)
t.Mores = make([]*More, 0)
t.Users = make([]*User, 0)
t.Posts = make([]*Post, 0)
t.Subreddits = make([]*Subreddit, 0)
t.ModActions = make([]*ModAction, 0)
t.Multis = make([]*Multi, 0)
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (t *things) UnmarshalJSON(b []byte) error {
t.init()
var things []thing
if err := json.Unmarshal(b, &things); err != nil {
return err
+170
View File
@@ -0,0 +1,170 @@
package reddit
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
// WikiService handles communication with the wiki
// related methods of the Reddit API.
//
// Reddit API docs: https://www.reddit.com/dev/api/#section_wiki
type WikiService struct {
client *Client
}
// WikiPagePermissionLevel defines who can edit a specific wiki page in a subreddit.
type WikiPagePermissionLevel int
const (
// PermissionSubredditWikiPermissions uses subreddit wiki permissions.
PermissionSubredditWikiPermissions WikiPagePermissionLevel = iota
// PermissionApprovedContributorsOnly is only for approved wiki contributors.
PermissionApprovedContributorsOnly
// PermissionModeratorsOnly is only for moderators.
PermissionModeratorsOnly
)
// WikiPageSettings holds the settings for a specific wiki page.
type WikiPageSettings struct {
PermissionLevel WikiPagePermissionLevel `json:"permlevel"`
Listed bool `json:"listed"`
Editors []*User `json:"editors"`
}
// WikiPageSettingsUpdateRequest represents a request to update the visibility and
// permissions of a wiki page.
type WikiPageSettingsUpdateRequest struct {
// This HAS to be provided no matter what, or else we get a 500 response.
PermissionLevel WikiPagePermissionLevel `url:"permlevel"`
Listed *bool `url:"listed,omitempty"`
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (s *WikiPageSettings) UnmarshalJSON(b []byte) error {
root := new(struct {
PermissionLevel WikiPagePermissionLevel `json:"permlevel"`
Listed bool `json:"listed"`
Things []thing `json:"editors"`
})
err := json.Unmarshal(b, root)
if err != nil {
return err
}
s.PermissionLevel = root.PermissionLevel
s.Listed = root.Listed
for _, thing := range root.Things {
if user, ok := thing.User(); ok {
s.Editors = append(s.Editors, user)
}
}
return nil
}
// Pages retrieves a list of wiki pages in the subreddit.
// Returns 403 Forbidden if the wiki is disabled.
func (s *WikiService) Pages(ctx context.Context, subreddit string) ([]string, *Response, error) {
path := fmt.Sprintf("r/%s/wiki/pages", 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
}
pages, _ := root.WikiPages()
return pages, resp, nil
}
// Settings gets the subreddit's wiki page's settings.
func (s *WikiService) Settings(ctx context.Context, subreddit, page string) (*WikiPageSettings, *Response, error) {
path := fmt.Sprintf("r/%s/wiki/settings/%s", subreddit, page)
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
}
settings, _ := root.WikiPageSettings()
return settings, resp, nil
}
// UpdateSettings updates the subreddit's wiki page's settings.
func (s *WikiService) UpdateSettings(ctx context.Context, subreddit, page string, updateRequest *WikiPageSettingsUpdateRequest) (*WikiPageSettings, *Response, error) {
if updateRequest == nil {
return nil, nil, errors.New("updateRequest: cannot be nil")
}
form, err := query.Values(updateRequest)
if err != nil {
return nil, nil, err
}
path := fmt.Sprintf("r/%s/wiki/settings/%s", subreddit, page)
req, err := s.client.NewRequestWithForm(http.MethodPost, path, form)
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
}
settings, _ := root.WikiPageSettings()
return settings, resp, nil
}
// Allow the user to edit the specified wiki page in the subreddit.
func (s *WikiService) Allow(ctx context.Context, subreddit, page, username string) (*Response, error) {
path := fmt.Sprintf("r/%s/api/wiki/alloweditor/add", subreddit)
form := url.Values{}
form.Set("page", page)
form.Set("username", username)
req, err := s.client.NewRequestWithForm(http.MethodPost, path, form)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// Deny the user the ability to edit the specified wiki page in the subreddit.
func (s *WikiService) Deny(ctx context.Context, subreddit, page, username string) (*Response, error) {
path := fmt.Sprintf("r/%s/api/wiki/alloweditor/del", subreddit)
form := url.Values{}
form.Set("page", page)
form.Set("username", username)
req, err := s.client.NewRequestWithForm(http.MethodPost, path, form)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
+138
View File
@@ -0,0 +1,138 @@
package reddit
import (
"fmt"
"net/http"
"net/url"
"testing"
"time"
"github.com/stretchr/testify/require"
)
var expectedWikiPageSettings = &WikiPageSettings{
PermissionLevel: PermissionSubredditWikiPermissions,
Listed: true,
Editors: []*User{
{
ID: "164ab8",
Name: "v_95",
Created: &Timestamp{time.Date(2017, 3, 12, 4, 56, 47, 0, time.UTC)},
PostKarma: 691,
CommentKarma: 22235,
HasVerifiedEmail: true,
NSFW: true,
},
},
}
func TestWikiService_Pages(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
mux.HandleFunc("/r/testsubreddit/wiki/pages", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodGet, r.Method)
fmt.Fprint(w, `{
"kind": "wikipagelisting",
"data": [
"faq",
"index"
]
}`)
})
pages, _, err := client.Wiki.Pages(ctx, "testsubreddit")
require.NoError(t, err)
require.Equal(t, []string{"faq", "index"}, pages)
}
func TestWikiService_Settings(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
blob, err := readFileContents("../testdata/wiki/page-settings.json")
require.NoError(t, err)
mux.HandleFunc("/r/testsubreddit/wiki/settings/testpage", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodGet, r.Method)
fmt.Fprint(w, blob)
})
settings, _, err := client.Wiki.Settings(ctx, "testsubreddit", "testpage")
require.NoError(t, err)
require.Equal(t, expectedWikiPageSettings, settings)
}
func TestWikiService_UpdateSettings(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
blob, err := readFileContents("../testdata/wiki/page-settings.json")
require.NoError(t, err)
mux.HandleFunc("/r/testsubreddit/wiki/settings/testpage", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
form := url.Values{}
form.Set("permlevel", "1")
form.Set("listed", "false")
err := r.ParseForm()
require.NoError(t, err)
require.Equal(t, form, r.PostForm)
fmt.Fprint(w, blob)
})
_, _, err = client.Wiki.UpdateSettings(ctx, "testsubreddit", "testpage", nil)
require.EqualError(t, err, "updateRequest: cannot be nil")
settings, _, err := client.Wiki.UpdateSettings(ctx, "testsubreddit", "testpage", &WikiPageSettingsUpdateRequest{
Listed: Bool(false),
PermissionLevel: PermissionApprovedContributorsOnly,
})
require.NoError(t, err)
require.Equal(t, expectedWikiPageSettings, settings)
}
func TestWikiService_Allow(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
mux.HandleFunc("/r/testsubreddit/api/wiki/alloweditor/add", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
form := url.Values{}
form.Set("page", "testpage")
form.Set("username", "testusername")
err := r.ParseForm()
require.NoError(t, err)
require.Equal(t, form, r.PostForm)
})
_, err := client.Wiki.Allow(ctx, "testsubreddit", "testpage", "testusername")
require.NoError(t, err)
}
func TestWikiService_Deny(t *testing.T) {
client, mux, teardown := setup()
defer teardown()
mux.HandleFunc("/r/testsubreddit/api/wiki/alloweditor/del", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
form := url.Values{}
form.Set("page", "testpage")
form.Set("username", "testusername")
err := r.ParseForm()
require.NoError(t, err)
require.Equal(t, form, r.PostForm)
})
_, err := client.Wiki.Deny(ctx, "testsubreddit", "testpage", "testusername")
require.NoError(t, err)
}