Reuse common service struct

Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
Vartan Benohanian 2020-06-27 23:53:59 -04:00
parent c8d7abf216
commit 33294aa036
18 changed files with 172 additions and 339 deletions

View file

@ -7,20 +7,9 @@ import (
// AccountService handles communication with the account
// related methods of the Reddit API.
type AccountService interface {
Info(ctx context.Context) (*User, *Response, error)
Karma(ctx context.Context) ([]SubredditKarma, *Response, error)
Settings(ctx context.Context) (*Settings, *Response, error)
UpdateSettings(ctx context.Context, settings *Settings) (*Settings, *Response, error)
Trophies(ctx context.Context) ([]Trophy, *Response, error)
}
// AccountServiceOp implements the AccountService interface.
type AccountServiceOp struct {
client *Client
}
var _ AccountService = &AccountServiceOp{}
//
// Reddit API docs: https://www.reddit.com/dev/api/#section_account
type AccountService service
type rootSubredditKarma struct {
Kind string `json:"kind,omitempty"`
@ -219,7 +208,7 @@ type Settings struct {
}
// Info returns some general information about your account.
func (s *AccountServiceOp) Info(ctx context.Context) (*User, *Response, error) {
func (s *AccountService) Info(ctx context.Context) (*User, *Response, error) {
path := "api/v1/me"
req, err := s.client.NewRequest(http.MethodGet, path, nil)
@ -237,7 +226,7 @@ func (s *AccountServiceOp) Info(ctx context.Context) (*User, *Response, error) {
}
// Karma returns a breakdown of your karma per subreddit.
func (s *AccountServiceOp) Karma(ctx context.Context) ([]SubredditKarma, *Response, error) {
func (s *AccountService) Karma(ctx context.Context) ([]SubredditKarma, *Response, error) {
path := "api/v1/me/karma"
req, err := s.client.NewRequest(http.MethodGet, path, nil)
@ -255,7 +244,7 @@ func (s *AccountServiceOp) Karma(ctx context.Context) ([]SubredditKarma, *Respon
}
// Settings returns your account settings.
func (s *AccountServiceOp) Settings(ctx context.Context) (*Settings, *Response, error) {
func (s *AccountService) Settings(ctx context.Context) (*Settings, *Response, error) {
path := "api/v1/me/prefs"
req, err := s.client.NewRequest(http.MethodGet, path, nil)
@ -273,7 +262,7 @@ func (s *AccountServiceOp) Settings(ctx context.Context) (*Settings, *Response,
}
// UpdateSettings updates your account settings and returns the modified version.
func (s *AccountServiceOp) UpdateSettings(ctx context.Context, settings *Settings) (*Settings, *Response, error) {
func (s *AccountService) UpdateSettings(ctx context.Context, settings *Settings) (*Settings, *Response, error) {
path := "api/v1/me/prefs"
req, err := s.client.NewRequest(http.MethodPatch, path, settings)
@ -291,7 +280,7 @@ func (s *AccountServiceOp) UpdateSettings(ctx context.Context, settings *Setting
}
// Trophies returns a list of your trophies.
func (s *AccountServiceOp) Trophies(ctx context.Context) ([]Trophy, *Response, error) {
func (s *AccountService) Trophies(ctx context.Context) ([]Trophy, *Response, error) {
path := "api/v1/me/trophies"
req, err := s.client.NewRequest(http.MethodGet, path, nil)

View file

@ -97,7 +97,7 @@ var expectedSettings = &Settings{
EnableVideoAutoplay: Bool(true),
}
func TestAccountServiceOp_Info(t *testing.T) {
func TestAccountService_Info(t *testing.T) {
setup()
defer teardown()
@ -113,7 +113,7 @@ func TestAccountServiceOp_Info(t *testing.T) {
assert.Equal(t, expectedInfo, info)
}
func TestAccountServiceOp_Karma(t *testing.T) {
func TestAccountService_Karma(t *testing.T) {
setup()
defer teardown()
@ -129,7 +129,7 @@ func TestAccountServiceOp_Karma(t *testing.T) {
assert.Equal(t, expectedKarma, karma)
}
func TestAccountServiceOp_Settings(t *testing.T) {
func TestAccountService_Settings(t *testing.T) {
setup()
defer teardown()
@ -145,7 +145,7 @@ func TestAccountServiceOp_Settings(t *testing.T) {
assert.Equal(t, expectedSettings, settings)
}
func TestAccountServiceOp_UpdateSettings(t *testing.T) {
func TestAccountService_UpdateSettings(t *testing.T) {
setup()
defer teardown()

View file

@ -9,28 +9,14 @@ import (
// CommentService handles communication with the comment
// related methods of the Reddit API.
type CommentService interface {
Submit(ctx context.Context, id string, text string) (*Comment, *Response, error)
Edit(ctx context.Context, id string, text string) (*Comment, *Response, error)
Delete(ctx context.Context, id string) (*Response, error)
type CommentService service
Save(ctx context.Context, id string) (*Response, error)
Unsave(ctx context.Context, id string) (*Response, error)
}
// CommentServiceOp implements the CommentService interface.
type CommentServiceOp struct {
client *Client
}
var _ CommentService = &CommentServiceOp{}
func (s *CommentServiceOp) isCommentID(id string) bool {
func (s *CommentService) isCommentID(id string) bool {
return strings.HasPrefix(id, kindComment+"_")
}
// Submit submits a comment as a reply to a post or to another comment.
func (s *CommentServiceOp) Submit(ctx context.Context, id string, text string) (*Comment, *Response, error) {
func (s *CommentService) Submit(ctx context.Context, id string, text string) (*Comment, *Response, error) {
path := "api/comment"
form := url.Values{}
@ -55,7 +41,7 @@ func (s *CommentServiceOp) Submit(ctx context.Context, id string, text string) (
// Edit edits the comment with the id provided.
// todo: don't forget to do this for posts
func (s *CommentServiceOp) Edit(ctx context.Context, id string, text string) (*Comment, *Response, error) {
func (s *CommentService) Edit(ctx context.Context, id string, text string) (*Comment, *Response, error) {
if !s.isCommentID(id) {
return nil, nil, fmt.Errorf("must provide comment id (starting with %s_); id provided: %q", kindComment, id)
}
@ -84,7 +70,7 @@ func (s *CommentServiceOp) Edit(ctx context.Context, id string, text string) (*C
// Delete deletes a comment via the id.
// todo: don't forget to do this for posts.
func (s *CommentServiceOp) Delete(ctx context.Context, id string) (*Response, error) {
func (s *CommentService) Delete(ctx context.Context, id string) (*Response, error) {
if !s.isCommentID(id) {
return nil, fmt.Errorf("must provide comment id (starting with %s_); id provided: %q", kindComment, id)
}
@ -108,7 +94,7 @@ func (s *CommentServiceOp) Delete(ctx context.Context, id string) (*Response, er
}
// Save saves a comment.
func (s *CommentServiceOp) Save(ctx context.Context, id string) (*Response, error) {
func (s *CommentService) Save(ctx context.Context, id string) (*Response, error) {
if !s.isCommentID(id) {
return nil, fmt.Errorf("must provide comment id (starting with %s_); id provided: %q", kindComment, id)
}
@ -132,7 +118,7 @@ func (s *CommentServiceOp) Save(ctx context.Context, id string) (*Response, erro
}
// Unsave unsaves a comment.
func (s *CommentServiceOp) Unsave(ctx context.Context, id string) (*Response, error) {
func (s *CommentService) Unsave(ctx context.Context, id string) (*Response, error) {
if !s.isCommentID(id) {
return nil, fmt.Errorf("must provide comment id (starting with t1_); id provided: %q", id)
}

View file

@ -38,11 +38,11 @@ var expectedCommentSubmitOrEdit = &Comment{
PostID: "t3_link1",
}
func TestCommentServiceOp_Submit(t *testing.T) {
func TestCommentService_Submit(t *testing.T) {
setup()
defer teardown()
commentBlob := readFileContents(t, "testdata/comment-submit-edit.json")
blob := readFileContents(t, "testdata/comment-submit-edit.json")
mux.HandleFunc("/api/comment", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodPost, r.Method)
@ -57,7 +57,7 @@ func TestCommentServiceOp_Submit(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, form, r.PostForm)
fmt.Fprint(w, commentBlob)
fmt.Fprint(w, blob)
})
comment, _, err := client.Comment.Submit(ctx, "t1_test", "test comment")
@ -65,11 +65,11 @@ func TestCommentServiceOp_Submit(t *testing.T) {
assert.Equal(t, expectedCommentSubmitOrEdit, comment)
}
func TestCommentServiceOp_Edit(t *testing.T) {
func TestCommentService_Edit(t *testing.T) {
setup()
defer teardown()
commentBlob := readFileContents(t, "testdata/comment-submit-edit.json")
blob := readFileContents(t, "testdata/comment-submit-edit.json")
mux.HandleFunc("/api/editusertext", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodPost, r.Method)
@ -84,7 +84,7 @@ func TestCommentServiceOp_Edit(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, form, r.PostForm)
fmt.Fprint(w, commentBlob)
fmt.Fprint(w, blob)
})
_, _, err := client.Comment.Edit(ctx, "t3_test", "test comment")
@ -95,7 +95,7 @@ func TestCommentServiceOp_Edit(t *testing.T) {
assert.Equal(t, expectedCommentSubmitOrEdit, comment)
}
func TestCommentServiceOp_Delete(t *testing.T) {
func TestCommentService_Delete(t *testing.T) {
setup()
defer teardown()
@ -120,7 +120,7 @@ func TestCommentServiceOp_Delete(t *testing.T) {
assert.Equal(t, http.StatusOK, res.StatusCode)
}
func TestCommentServiceOp_Save(t *testing.T) {
func TestCommentService_Save(t *testing.T) {
setup()
defer teardown()
@ -133,8 +133,6 @@ func TestCommentServiceOp_Save(t *testing.T) {
err := r.ParseForm()
assert.NoError(t, err)
assert.Equal(t, form, r.PostForm)
fmt.Fprint(w, `{}`)
})
_, err := client.Comment.Save(ctx, "t3_test")
@ -145,7 +143,7 @@ func TestCommentServiceOp_Save(t *testing.T) {
assert.Equal(t, http.StatusOK, res.StatusCode)
}
func TestCommentServiceOp_Unsave(t *testing.T) {
func TestCommentService_Unsave(t *testing.T) {
setup()
defer teardown()
@ -158,8 +156,6 @@ func TestCommentServiceOp_Unsave(t *testing.T) {
err := r.ParseForm()
assert.NoError(t, err)
assert.Equal(t, form, r.PostForm)
fmt.Fprint(w, `{}`)
})
_, err := client.Comment.Unsave(ctx, "t3_test")

View file

@ -6,19 +6,11 @@ import (
"net/http"
)
// FlairService handles communication with the user
// related methods of the Reddit API
type FlairService interface {
GetFromSubreddit(ctx context.Context, name string) ([]Flair, *Response, error)
GetFromSubredditV2(ctx context.Context, name string) ([]FlairV2, *Response, error)
}
// FlairServiceOp implements the FlairService interface
type FlairServiceOp struct {
client *Client
}
var _ FlairService = &FlairServiceOp{}
// FlairService handles communication with the flair
// related methods of the Reddit API.
//
// Reddit API docs: https://www.reddit.com/dev/api/#section_flair
type FlairService service
// Flair is a flair on Reddit
type Flair struct {
@ -38,7 +30,7 @@ type FlairV2 struct {
}
// GetFromSubreddit returns the flairs from the subreddit
func (s *FlairServiceOp) GetFromSubreddit(ctx context.Context, name string) ([]Flair, *Response, error) {
func (s *FlairService) GetFromSubreddit(ctx context.Context, name string) ([]Flair, *Response, error) {
path := fmt.Sprintf("r/%s/api/user_flair", name)
req, err := s.client.NewRequest(http.MethodGet, path, nil)
@ -56,7 +48,7 @@ func (s *FlairServiceOp) GetFromSubreddit(ctx context.Context, name string) ([]F
}
// GetFromSubredditV2 returns the flairs from the subreddit
func (s *FlairServiceOp) GetFromSubredditV2(ctx context.Context, name string) ([]FlairV2, *Response, error) {
func (s *FlairService) GetFromSubredditV2(ctx context.Context, name string) ([]FlairV2, *Response, error) {
path := fmt.Sprintf("r/%s/api/user_flair_v2", name)
req, err := s.client.NewRequest(http.MethodGet, path, nil)

View file

@ -40,15 +40,15 @@ var expectedFlairsV2 = []FlairV2{
},
}
func TestFlairServiceOp_GetFlairs(t *testing.T) {
func TestFlairService_GetFlairs(t *testing.T) {
setup()
defer teardown()
flairsBlob := readFileContents(t, "testdata/flairs.json")
blob := readFileContents(t, "testdata/flairs.json")
mux.HandleFunc("/r/subreddit/api/user_flair", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodGet, r.Method)
fmt.Fprint(w, flairsBlob)
fmt.Fprint(w, blob)
})
flairs, _, err := client.Flair.GetFromSubreddit(ctx, "subreddit")
@ -56,15 +56,15 @@ func TestFlairServiceOp_GetFlairs(t *testing.T) {
assert.Equal(t, expectedFlairs, flairs)
}
func TestFlairServiceOp_GetFlairsV2(t *testing.T) {
func TestFlairService_GetFlairsV2(t *testing.T) {
setup()
defer teardown()
flairsV2Blob := readFileContents(t, "testdata/flairs-v2.json")
blob := readFileContents(t, "testdata/flairs-v2.json")
mux.HandleFunc("/r/subreddit/api/user_flair_v2", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodGet, r.Method)
fmt.Fprint(w, flairsV2Blob)
fmt.Fprint(w, blob)
})
flairs, _, err := client.Flair.GetFromSubredditV2(ctx, "subreddit")

View file

@ -91,21 +91,28 @@ type Client struct {
// This is the client's user ID in Reddit's database.
redditID string
Account AccountService
Comment CommentService
Flair FlairService
Listings ListingsService
Post PostService
Search SearchService
Subreddit SubredditService
User UserService
Vote VoteService
// Reuse a single struct instead of allocating one for each service on the heap.
common service
Account *AccountService
Comment *CommentService
Flair *FlairService
Listings *ListingsService
Post *PostService
Search *SearchService
Subreddit *SubredditService
User *UserService
Vote *VoteService
oauth2Transport *oauth2.Transport
onRequestCompleted RequestCompletionCallback
}
type service struct {
client *Client
}
// OnRequestCompleted sets the client's request completion callback.
func (c *Client) OnRequestCompleted(rc RequestCompletionCallback) {
c.onRequestCompleted = rc
@ -121,15 +128,16 @@ func newClient(httpClient *http.Client) *Client {
c := &Client{client: httpClient, BaseURL: baseURL, TokenURL: tokenURL}
c.Account = &AccountServiceOp{client: c}
c.Comment = &CommentServiceOp{client: c}
c.Flair = &FlairServiceOp{client: c}
c.Listings = &ListingsServiceOp{client: c}
c.Post = &PostServiceOp{client: c}
c.Search = &SearchServiceOp{client: c}
c.Subreddit = &SubredditServiceOp{client: c}
c.User = &UserServiceOp{client: c}
c.Vote = &VoteServiceOp{client: c}
c.common.client = c
c.Account = (*AccountService)(&c.common)
c.Comment = (*CommentService)(&c.common)
c.Flair = (*FlairService)(&c.common)
c.Listings = (*ListingsService)(&c.common)
c.Post = (*PostService)(&c.common)
c.Search = (*SearchService)(&c.common)
c.Subreddit = (*SubredditService)(&c.common)
c.User = (*UserService)(&c.common)
c.Vote = (*VoteService)(&c.common)
return c
}

View file

@ -66,10 +66,12 @@ func readFileContents(t *testing.T, filepath string) string {
func testClientServices(t *testing.T, c *Client) {
services := []string{
"Account",
"Comment",
"Flair",
"Listings",
"Post",
"Search",
"Subreddit",
"User",
"Vote",

View file

@ -8,23 +8,14 @@ import (
"strings"
)
// ListingsService handles communication with the post
// ListingsService handles communication with the listing
// related methods of the Reddit API.
type ListingsService interface {
Get(ctx context.Context, ids ...string) ([]Post, []Comment, []Subreddit, *Response, error)
GetPosts(ctx context.Context, ids ...string) ([]Post, *Response, error)
GetPost(ctx context.Context, id string) (*PostAndComments, *Response, error)
}
// ListingsServiceOp implements the Vote interface.
type ListingsServiceOp struct {
client *Client
}
var _ ListingsService = &ListingsServiceOp{}
//
// Reddit API docs: https://www.reddit.com/dev/api/#section_listings
type ListingsService service
// Get returns posts, comments, and subreddits from their IDs.
func (s *ListingsServiceOp) Get(ctx context.Context, ids ...string) ([]Post, []Comment, []Subreddit, *Response, error) {
func (s *ListingsService) Get(ctx context.Context, ids ...string) ([]Post, []Comment, []Subreddit, *Response, error) {
type query struct {
IDs []string `url:"id,comma"`
}
@ -54,7 +45,7 @@ func (s *ListingsServiceOp) Get(ctx context.Context, ids ...string) ([]Post, []C
}
// GetPosts returns posts from their full IDs.
func (s *ListingsServiceOp) GetPosts(ctx context.Context, ids ...string) ([]Post, *Response, error) {
func (s *ListingsService) GetPosts(ctx context.Context, ids ...string) ([]Post, *Response, error) {
if len(ids) == 0 {
return nil, nil, errors.New("must provide at least 1 id")
}
@ -77,7 +68,7 @@ func (s *ListingsServiceOp) GetPosts(ctx context.Context, ids ...string) ([]Post
// GetPost returns a post with its comments.
// The id here is the ID36 of the post, not its full id.
// Example: instead of t3_abc123, use abc123.
func (s *ListingsServiceOp) GetPost(ctx context.Context, id string) (*PostAndComments, *Response, error) {
func (s *ListingsService) GetPost(ctx context.Context, id string) (*PostAndComments, *Response, error) {
path := fmt.Sprintf("comments/%s", id)
req, err := s.client.NewRequest(http.MethodGet, path, nil)
if err != nil {

48
post.go
View file

@ -9,31 +9,9 @@ import (
"github.com/google/go-querystring/query"
)
// PostService handles communication with the link (post)
// PostService handles communication with the post
// related methods of the Reddit API.
type PostService interface {
SubmitSelf(ctx context.Context, opts SubmitSelfOptions) (*Submitted, *Response, error)
SubmitURL(ctx context.Context, opts SubmitURLOptions) (*Submitted, *Response, error)
EnableReplies(ctx context.Context, id string) (*Response, error)
DisableReplies(ctx context.Context, id string) (*Response, error)
MarkNSFW(ctx context.Context, id string) (*Response, error)
UnmarkNSFW(ctx context.Context, id string) (*Response, error)
Spoiler(ctx context.Context, id string) (*Response, error)
Unspoiler(ctx context.Context, id string) (*Response, error)
Hide(ctx context.Context, ids ...string) (*Response, error)
Unhide(ctx context.Context, ids ...string) (*Response, error)
}
// PostServiceOp implements the PostService interface.
type PostServiceOp struct {
client *Client
}
var _ PostService = &PostServiceOp{}
type PostService service
type submittedLinkRoot struct {
JSON struct {
@ -78,7 +56,7 @@ type SubmitURLOptions struct {
}
// SubmitSelf submits a self text post.
func (s *PostServiceOp) SubmitSelf(ctx context.Context, opts SubmitSelfOptions) (*Submitted, *Response, error) {
func (s *PostService) SubmitSelf(ctx context.Context, opts SubmitSelfOptions) (*Submitted, *Response, error) {
type submit struct {
SubmitSelfOptions
Kind string `url:"kind,omitempty"`
@ -87,7 +65,7 @@ func (s *PostServiceOp) SubmitSelf(ctx context.Context, opts SubmitSelfOptions)
}
// SubmitURL submits a link post.
func (s *PostServiceOp) SubmitURL(ctx context.Context, opts SubmitURLOptions) (*Submitted, *Response, error) {
func (s *PostService) SubmitURL(ctx context.Context, opts SubmitURLOptions) (*Submitted, *Response, error) {
type submit struct {
SubmitURLOptions
Kind string `url:"kind,omitempty"`
@ -95,7 +73,7 @@ func (s *PostServiceOp) SubmitURL(ctx context.Context, opts SubmitURLOptions) (*
return s.submit(ctx, &submit{opts, "link"})
}
func (s *PostServiceOp) submit(ctx context.Context, v interface{}) (*Submitted, *Response, error) {
func (s *PostService) submit(ctx context.Context, v interface{}) (*Submitted, *Response, error) {
path := "api/submit"
form, err := query.Values(v)
@ -119,7 +97,7 @@ func (s *PostServiceOp) submit(ctx context.Context, v interface{}) (*Submitted,
}
// EnableReplies enables inbox replies for a thing created by the client.
func (s *PostServiceOp) EnableReplies(ctx context.Context, id string) (*Response, error) {
func (s *PostService) EnableReplies(ctx context.Context, id string) (*Response, error) {
path := "api/sendreplies"
form := url.Values{}
@ -135,7 +113,7 @@ func (s *PostServiceOp) EnableReplies(ctx context.Context, id string) (*Response
}
// DisableReplies dsables inbox replies for a thing created by the client.
func (s *PostServiceOp) DisableReplies(ctx context.Context, id string) (*Response, error) {
func (s *PostService) DisableReplies(ctx context.Context, id string) (*Response, error) {
path := "api/sendreplies"
form := url.Values{}
@ -151,7 +129,7 @@ func (s *PostServiceOp) DisableReplies(ctx context.Context, id string) (*Respons
}
// MarkNSFW marks a post as NSFW.
func (s *PostServiceOp) MarkNSFW(ctx context.Context, id string) (*Response, error) {
func (s *PostService) MarkNSFW(ctx context.Context, id string) (*Response, error) {
path := "api/marknsfw"
form := url.Values{}
@ -166,7 +144,7 @@ func (s *PostServiceOp) MarkNSFW(ctx context.Context, id string) (*Response, err
}
// UnmarkNSFW unmarks a post as NSFW.
func (s *PostServiceOp) UnmarkNSFW(ctx context.Context, id string) (*Response, error) {
func (s *PostService) UnmarkNSFW(ctx context.Context, id string) (*Response, error) {
path := "api/unmarknsfw"
form := url.Values{}
@ -181,7 +159,7 @@ func (s *PostServiceOp) UnmarkNSFW(ctx context.Context, id string) (*Response, e
}
// Spoiler marks a post as a spoiler.
func (s *PostServiceOp) Spoiler(ctx context.Context, id string) (*Response, error) {
func (s *PostService) Spoiler(ctx context.Context, id string) (*Response, error) {
path := "api/spoiler"
form := url.Values{}
@ -196,7 +174,7 @@ func (s *PostServiceOp) Spoiler(ctx context.Context, id string) (*Response, erro
}
// Unspoiler unmarks a post as a spoiler.
func (s *PostServiceOp) Unspoiler(ctx context.Context, id string) (*Response, error) {
func (s *PostService) Unspoiler(ctx context.Context, id string) (*Response, error) {
path := "api/unspoiler"
form := url.Values{}
@ -211,7 +189,7 @@ func (s *PostServiceOp) Unspoiler(ctx context.Context, id string) (*Response, er
}
// Hide hides links with the specified ids.
func (s *PostServiceOp) Hide(ctx context.Context, ids ...string) (*Response, error) {
func (s *PostService) Hide(ctx context.Context, ids ...string) (*Response, error) {
if len(ids) == 0 {
return nil, errors.New("must provide at least 1 id")
}
@ -230,7 +208,7 @@ func (s *PostServiceOp) Hide(ctx context.Context, ids ...string) (*Response, err
}
// Unhide unhides links with the specified ids.
func (s *PostServiceOp) Unhide(ctx context.Context, ids ...string) (*Response, error) {
func (s *PostService) Unhide(ctx context.Context, ids ...string) (*Response, error) {
if len(ids) == 0 {
return nil, errors.New("must provide at least 1 id")
}

View file

@ -1,7 +1,6 @@
package geddit
import (
"fmt"
"net/http"
"net/url"
"testing"
@ -9,7 +8,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestPostServiceOp_Hide(t *testing.T) {
func TestPostService_Hide(t *testing.T) {
setup()
defer teardown()
@ -22,8 +21,6 @@ func TestPostServiceOp_Hide(t *testing.T) {
err := r.ParseForm()
assert.NoError(t, err)
assert.Equal(t, form, r.PostForm)
fmt.Fprint(w, `{}`)
})
_, err := client.Post.Hide(ctx)
@ -34,7 +31,7 @@ func TestPostServiceOp_Hide(t *testing.T) {
assert.Equal(t, http.StatusOK, res.StatusCode)
}
func TestPostServiceOp_Unhide(t *testing.T) {
func TestPostService_Unhide(t *testing.T) {
setup()
defer teardown()
@ -47,8 +44,6 @@ func TestPostServiceOp_Unhide(t *testing.T) {
err := r.ParseForm()
assert.NoError(t, err)
assert.Equal(t, form, r.PostForm)
fmt.Fprint(w, `{}`)
})
_, err := client.Post.Unhide(ctx)

View file

@ -9,24 +9,16 @@ import (
)
// SearchService handles communication with the search
// related methods of the Reddit API
// IMPORTANT: for searches to include NSFW results, the
// user must check the following in their preferences:
// related methods of the Reddit API.
//
// For searches to include NSFW results, the user must
// enable the following setting in their preferences:
// "include not safe for work (NSFW) search results in searches"
// Note: The "limit" parameter in searches is prone to inconsistent
// behaviour.
type SearchService interface {
Posts(ctx context.Context, query string, subreddits []string, opts ...SearchOptionSetter) (*Posts, *Response, error)
Subreddits(ctx context.Context, query string, opts ...SearchOptionSetter) (*Subreddits, *Response, error)
Users(ctx context.Context, query string, opts ...SearchOptionSetter) (*Users, *Response, error)
}
// SearchServiceOp implements the VoteService interface
type SearchServiceOp struct {
client *Client
}
var _ SearchService = &SearchServiceOp{}
// behaviour, e.g. sometimes limit=1 returns nothing when it should.
//
// Reddit API docs: https://www.reddit.com/dev/api/#section_search
type SearchService service
// SearchOptions define options used in search queries.
type SearchOptions = url.Values
@ -98,8 +90,8 @@ func setRestrict(opts SearchOptions) {
}
// Posts searches for posts.
// If the list of subreddits is empty, the search is run against r/all.
func (s *SearchServiceOp) Posts(ctx context.Context, query string, subreddits []string, opts ...SearchOptionSetter) (*Posts, *Response, error) {
// If the list of subreddits provided is empty, the search is run against r/all.
func (s *SearchService) Posts(ctx context.Context, query string, subreddits []string, opts ...SearchOptionSetter) (*Posts, *Response, error) {
opts = append(opts, setType("link"), setQuery(query))
path := "search"
@ -127,7 +119,7 @@ func (s *SearchServiceOp) Posts(ctx context.Context, query string, subreddits []
// Subreddits searches for subreddits.
// The Sort and Timespan options don't affect the results for this search.
func (s *SearchServiceOp) Subreddits(ctx context.Context, query string, opts ...SearchOptionSetter) (*Subreddits, *Response, error) {
func (s *SearchService) Subreddits(ctx context.Context, query string, opts ...SearchOptionSetter) (*Subreddits, *Response, error) {
opts = append(opts, setType("sr"), setQuery(query))
form := newSearchOptions(opts...)
@ -150,7 +142,7 @@ func (s *SearchServiceOp) Subreddits(ctx context.Context, query string, opts ...
// Users searches for users.
// The Sort and Timespan options don't affect the results for this search.
func (s *SearchServiceOp) Users(ctx context.Context, query string, opts ...SearchOptionSetter) (*Users, *Response, error) {
func (s *SearchService) Users(ctx context.Context, query string, opts ...SearchOptionSetter) (*Users, *Response, error) {
opts = append(opts, setType("user"), setQuery(query))
form := newSearchOptions(opts...)

View file

@ -10,39 +10,10 @@ import (
)
// SubredditService handles communication with the subreddit
// related methods of the Reddit API
type SubredditService interface {
GetPosts() *PostFinder
GetByName(ctx context.Context, subreddit string) (*Subreddit, *Response, error)
GetPopular(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error)
GetNew(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error)
GetGold(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error)
GetDefault(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error)
GetSubscribed(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error)
GetApproved(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error)
GetModerated(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error)
GetSticky1(ctx context.Context, subreddit string) (*PostAndComments, *Response, error)
GetSticky2(ctx context.Context, subreddit string) (*PostAndComments, *Response, error)
Subscribe(ctx context.Context, subreddits ...string) (*Response, error)
SubscribeByID(ctx context.Context, ids ...string) (*Response, error)
Unsubscribe(ctx context.Context, subreddits ...string) (*Response, error)
UnsubscribeByID(ctx context.Context, ids ...string) (*Response, error)
SearchSubredditNames(ctx context.Context, query string) ([]string, *Response, error)
SearchSubredditInfo(ctx context.Context, query string) ([]SubredditShort, *Response, error)
}
// SubredditServiceOp implements the SubredditService interface
type SubredditServiceOp struct {
client *Client
}
var _ SubredditService = &SubredditServiceOp{}
// related methods of the Reddit API.
//
// Reddit API docs: https://www.reddit.com/dev/api/#section_subreddits
type SubredditService service
type subredditNamesRoot struct {
Names []string `json:"names,omitempty"`
@ -63,14 +34,14 @@ type SubredditShort struct {
// By default, it'll look for the hottest posts from r/all.
// Note: when looking for hot posts in a subreddit, it will include the
// sticked posts (if any) PLUS posts from the limit parameter (25 by default).
func (s *SubredditServiceOp) GetPosts() *PostFinder {
func (s *SubredditService) GetPosts() *PostFinder {
f := new(PostFinder)
f.client = s.client
return f.Sort(SortHot).FromAll()
}
// GetByName gets a subreddit by name
func (s *SubredditServiceOp) GetByName(ctx context.Context, subreddit string) (*Subreddit, *Response, error) {
func (s *SubredditService) GetByName(ctx context.Context, subreddit string) (*Subreddit, *Response, error) {
if subreddit == "" {
return nil, nil, errors.New("empty subreddit name provided")
}
@ -91,52 +62,52 @@ func (s *SubredditServiceOp) GetByName(ctx context.Context, subreddit string) (*
}
// GetPopular returns popular subreddits.
func (s *SubredditServiceOp) GetPopular(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
func (s *SubredditService) GetPopular(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
return s.getSubreddits(ctx, "subreddits/popular", opts)
}
// GetNew returns new subreddits.
func (s *SubredditServiceOp) GetNew(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
func (s *SubredditService) GetNew(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
return s.getSubreddits(ctx, "subreddits/new", opts)
}
// GetGold returns gold subreddits.
func (s *SubredditServiceOp) GetGold(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
func (s *SubredditService) GetGold(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
return s.getSubreddits(ctx, "subreddits/gold", opts)
}
// GetDefault returns default subreddits.
func (s *SubredditServiceOp) GetDefault(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
func (s *SubredditService) GetDefault(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
return s.getSubreddits(ctx, "subreddits/default", opts)
}
// GetSubscribed returns the list of subreddits the client is subscribed to.
func (s *SubredditServiceOp) GetSubscribed(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
func (s *SubredditService) GetSubscribed(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
return s.getSubreddits(ctx, "subreddits/mine/subscriber", opts)
}
// GetApproved returns the list of subreddits the client is an approved user in.
func (s *SubredditServiceOp) GetApproved(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
func (s *SubredditService) GetApproved(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
return s.getSubreddits(ctx, "subreddits/mine/contributor", opts)
}
// GetModerated returns the list of subreddits the client is a moderator of.
func (s *SubredditServiceOp) GetModerated(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
func (s *SubredditService) GetModerated(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) {
return s.getSubreddits(ctx, "subreddits/mine/moderator", opts)
}
// GetSticky1 returns the first stickied post on a subreddit (if it exists).
func (s *SubredditServiceOp) GetSticky1(ctx context.Context, name string) (*PostAndComments, *Response, error) {
func (s *SubredditService) GetSticky1(ctx context.Context, name string) (*PostAndComments, *Response, error) {
return s.getSticky(ctx, name, 1)
}
// GetSticky2 returns the second stickied post on a subreddit (if it exists).
func (s *SubredditServiceOp) GetSticky2(ctx context.Context, name string) (*PostAndComments, *Response, error) {
func (s *SubredditService) GetSticky2(ctx context.Context, name string) (*PostAndComments, *Response, error) {
return s.getSticky(ctx, name, 2)
}
// Subscribe subscribes to subreddits based on their names.
func (s *SubredditServiceOp) Subscribe(ctx context.Context, subreddits ...string) (*Response, error) {
func (s *SubredditService) Subscribe(ctx context.Context, subreddits ...string) (*Response, error) {
form := url.Values{}
form.Set("action", "sub")
form.Set("sr_name", strings.Join(subreddits, ","))
@ -144,7 +115,7 @@ func (s *SubredditServiceOp) Subscribe(ctx context.Context, subreddits ...string
}
// SubscribeByID subscribes to subreddits based on their id.
func (s *SubredditServiceOp) SubscribeByID(ctx context.Context, ids ...string) (*Response, error) {
func (s *SubredditService) SubscribeByID(ctx context.Context, ids ...string) (*Response, error) {
form := url.Values{}
form.Set("action", "sub")
form.Set("sr", strings.Join(ids, ","))
@ -152,7 +123,7 @@ func (s *SubredditServiceOp) SubscribeByID(ctx context.Context, ids ...string) (
}
// Unsubscribe unsubscribes from subreddits based on their names.
func (s *SubredditServiceOp) Unsubscribe(ctx context.Context, subreddits ...string) (*Response, error) {
func (s *SubredditService) Unsubscribe(ctx context.Context, subreddits ...string) (*Response, error) {
form := url.Values{}
form.Set("action", "unsub")
form.Set("sr_name", strings.Join(subreddits, ","))
@ -160,7 +131,7 @@ func (s *SubredditServiceOp) Unsubscribe(ctx context.Context, subreddits ...stri
}
// UnsubscribeByID unsubscribes from subreddits based on their id.
func (s *SubredditServiceOp) UnsubscribeByID(ctx context.Context, ids ...string) (*Response, error) {
func (s *SubredditService) UnsubscribeByID(ctx context.Context, ids ...string) (*Response, error) {
form := url.Values{}
form.Set("action", "unsub")
form.Set("sr", strings.Join(ids, ","))
@ -168,7 +139,7 @@ func (s *SubredditServiceOp) UnsubscribeByID(ctx context.Context, ids ...string)
}
// SearchSubredditNames searches for subreddits with names beginning with the query provided.
func (s *SubredditServiceOp) SearchSubredditNames(ctx context.Context, query string) ([]string, *Response, error) {
func (s *SubredditService) SearchSubredditNames(ctx context.Context, query string) ([]string, *Response, error) {
path := fmt.Sprintf("api/search_reddit_names?query=%s", query)
req, err := s.client.NewRequest(http.MethodGet, path, nil)
@ -187,7 +158,7 @@ func (s *SubredditServiceOp) SearchSubredditNames(ctx context.Context, query str
// SearchSubredditInfo searches for subreddits with names beginning with the query provided.
// They hold a bit more info that just the name, but still not much.
func (s *SubredditServiceOp) SearchSubredditInfo(ctx context.Context, query string) ([]SubredditShort, *Response, error) {
func (s *SubredditService) SearchSubredditInfo(ctx context.Context, query string) ([]SubredditShort, *Response, error) {
path := fmt.Sprintf("api/search_subreddits?query=%s", query)
req, err := s.client.NewRequest(http.MethodPost, path, nil)
@ -204,7 +175,7 @@ func (s *SubredditServiceOp) SearchSubredditInfo(ctx context.Context, query stri
return root.Subreddits, resp, nil
}
func (s *SubredditServiceOp) handleSubscription(ctx context.Context, form url.Values) (*Response, error) {
func (s *SubredditService) handleSubscription(ctx context.Context, form url.Values) (*Response, error) {
path := "api/subscribe"
req, err := s.client.NewPostForm(path, form)
if err != nil {
@ -219,7 +190,7 @@ func (s *SubredditServiceOp) handleSubscription(ctx context.Context, form url.Va
return resp, nil
}
func (s *SubredditServiceOp) getSubreddits(ctx context.Context, path string, opts *ListOptions) (*Subreddits, *Response, error) {
func (s *SubredditService) getSubreddits(ctx context.Context, path string, opts *ListOptions) (*Subreddits, *Response, error) {
path, err := addOptions(path, opts)
if err != nil {
return nil, nil, err
@ -249,7 +220,7 @@ func (s *SubredditServiceOp) getSubreddits(ctx context.Context, path string, opt
// getSticky returns one of the 2 stickied posts of the subreddit (if they exist).
// Num should be equal to 1 or 2, depending on which one you want.
func (s *SubredditServiceOp) getSticky(ctx context.Context, subreddit string, num int) (*PostAndComments, *Response, error) {
func (s *SubredditService) getSticky(ctx context.Context, subreddit string, num int) (*PostAndComments, *Response, error) {
type query struct {
Num int `url:"num"`
}

View file

@ -110,7 +110,7 @@ var expectedSticky = &PostAndComments{
},
}
func TestSubredditServiceOp_GetByName(t *testing.T) {
func TestSubredditService_GetByName(t *testing.T) {
setup()
defer teardown()
@ -126,7 +126,7 @@ func TestSubredditServiceOp_GetByName(t *testing.T) {
assert.Equal(t, expectedSubreddit, subreddit)
}
func TestSubredditServiceOp_GetPopular(t *testing.T) {
func TestSubredditService_GetPopular(t *testing.T) {
setup()
defer teardown()
@ -142,7 +142,7 @@ func TestSubredditServiceOp_GetPopular(t *testing.T) {
assert.Equal(t, expectedSubreddits, subreddits)
}
func TestSubredditServiceOp_GetNew(t *testing.T) {
func TestSubredditService_GetNew(t *testing.T) {
setup()
defer teardown()
@ -158,7 +158,7 @@ func TestSubredditServiceOp_GetNew(t *testing.T) {
assert.Equal(t, expectedSubreddits, subreddits)
}
func TestSubredditServiceOp_GetGold(t *testing.T) {
func TestSubredditService_GetGold(t *testing.T) {
setup()
defer teardown()
@ -174,7 +174,7 @@ func TestSubredditServiceOp_GetGold(t *testing.T) {
assert.Equal(t, expectedSubreddits, subreddits)
}
func TestSubredditServiceOp_GetDefault(t *testing.T) {
func TestSubredditService_GetDefault(t *testing.T) {
setup()
defer teardown()
@ -190,7 +190,7 @@ func TestSubredditServiceOp_GetDefault(t *testing.T) {
assert.Equal(t, expectedSubreddits, subreddits)
}
func TestSubredditServiceOp_GetSubscribed(t *testing.T) {
func TestSubredditService_GetSubscribed(t *testing.T) {
setup()
defer teardown()
@ -206,7 +206,7 @@ func TestSubredditServiceOp_GetSubscribed(t *testing.T) {
assert.Equal(t, expectedSubreddits, subreddits)
}
func TestSubredditServiceOp_GetApproved(t *testing.T) {
func TestSubredditService_GetApproved(t *testing.T) {
setup()
defer teardown()
@ -222,7 +222,7 @@ func TestSubredditServiceOp_GetApproved(t *testing.T) {
assert.Equal(t, expectedSubreddits, subreddits)
}
func TestSubredditServiceOp_GetModerated(t *testing.T) {
func TestSubredditService_GetModerated(t *testing.T) {
setup()
defer teardown()
@ -239,7 +239,7 @@ func TestSubredditServiceOp_GetModerated(t *testing.T) {
}
// todo: WIP
func TestSubredditServiceOp_GetSticky1(t *testing.T) {
func TestSubredditService_GetSticky1(t *testing.T) {
setup()
defer teardown()

90
user.go
View file

@ -9,47 +9,9 @@ import (
// UserService handles communication with the user
// related methods of the Reddit API.
type UserService interface {
Get(ctx context.Context, username string) (*User, *Response, error)
GetMultipleByID(ctx context.Context, ids ...string) (map[string]*UserShort, *Response, error)
UsernameAvailable(ctx context.Context, username string) (bool, *Response, error)
Overview(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error)
OverviewOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error)
Posts(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error)
PostsOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Posts, *Response, error)
Comments(ctx context.Context, opts ...SearchOptionSetter) (*Comments, *Response, error)
CommentsOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Comments, *Response, error)
Saved(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error)
// todo: votes can be public (they're private by default) so make UpvotedOf and DownvotedOf as well
Upvoted(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error)
Downvoted(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error)
Hidden(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error)
Gilded(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error)
GetFriendship(ctx context.Context, username string) (*Friendship, *Response, error)
Friend(ctx context.Context, username string) (*Friendship, *Response, error)
Unfriend(ctx context.Context, username string) (*Response, error)
Block(ctx context.Context, username string) (*Blocked, *Response, error)
// todo: decide if you wanna keep these
// BlockByID(ctx context.Context, id string) (*Blocked, *Response, error)
Unblock(ctx context.Context, username string) (*Response, error)
// UnblockByID(ctx context.Context, id string) (*Response, error)
Trophies(ctx context.Context) ([]Trophy, *Response, error)
TrophiesOf(ctx context.Context, username string) ([]Trophy, *Response, error)
}
// UserServiceOp implements the UserService interface.
type UserServiceOp struct {
client *Client
}
var _ UserService = &UserServiceOp{}
//
// Reddit API docs: https://www.reddit.com/dev/api/#section_users
type UserService service
// User represents a Reddit user.
type User struct {
@ -115,7 +77,7 @@ type Trophy struct {
}
// Get returns information about the user.
func (s *UserServiceOp) Get(ctx context.Context, username string) (*User, *Response, error) {
func (s *UserService) Get(ctx context.Context, username string) (*User, *Response, error) {
path := fmt.Sprintf("user/%s/about", username)
req, err := s.client.NewRequest(http.MethodGet, path, nil)
if err != nil {
@ -133,7 +95,7 @@ func (s *UserServiceOp) Get(ctx context.Context, username string) (*User, *Respo
// GetMultipleByID returns multiple users from their full IDs.
// The response body is a map where the keys are the IDs (if they exist), and the value is the user
func (s *UserServiceOp) GetMultipleByID(ctx context.Context, ids ...string) (map[string]*UserShort, *Response, error) {
func (s *UserService) GetMultipleByID(ctx context.Context, ids ...string) (map[string]*UserShort, *Response, error) {
type query struct {
IDs []string `url:"ids,omitempty,comma"`
}
@ -159,7 +121,7 @@ func (s *UserServiceOp) GetMultipleByID(ctx context.Context, ids ...string) (map
}
// UsernameAvailable checks whether a username is available for registration.
func (s *UserServiceOp) UsernameAvailable(ctx context.Context, username string) (bool, *Response, error) {
func (s *UserService) UsernameAvailable(ctx context.Context, username string) (bool, *Response, error) {
type query struct {
User string `url:"user,omitempty"`
}
@ -185,12 +147,12 @@ func (s *UserServiceOp) UsernameAvailable(ctx context.Context, username string)
}
// Overview returns a list of the client's posts and comments.
func (s *UserServiceOp) Overview(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error) {
func (s *UserService) Overview(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error) {
return s.OverviewOf(ctx, s.client.Username, opts...)
}
// OverviewOf returns a list of the user's posts and comments.
func (s *UserServiceOp) OverviewOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error) {
func (s *UserService) OverviewOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error) {
form := newSearchOptions(opts...)
path := fmt.Sprintf("user/%s/overview", username)
@ -211,12 +173,12 @@ func (s *UserServiceOp) OverviewOf(ctx context.Context, username string, opts ..
}
// Posts returns a list of the client's posts.
func (s *UserServiceOp) Posts(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) {
func (s *UserService) Posts(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) {
return s.PostsOf(ctx, s.client.Username, opts...)
}
// PostsOf returns a list of the user's posts.
func (s *UserServiceOp) PostsOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Posts, *Response, error) {
func (s *UserService) PostsOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Posts, *Response, error) {
form := newSearchOptions(opts...)
path := fmt.Sprintf("user/%s/submitted", username)
@ -237,12 +199,12 @@ func (s *UserServiceOp) PostsOf(ctx context.Context, username string, opts ...Se
}
// Comments returns a list of the client's comments.
func (s *UserServiceOp) Comments(ctx context.Context, opts ...SearchOptionSetter) (*Comments, *Response, error) {
func (s *UserService) Comments(ctx context.Context, opts ...SearchOptionSetter) (*Comments, *Response, error) {
return s.CommentsOf(ctx, s.client.Username, opts...)
}
// CommentsOf returns a list of the user's comments.
func (s *UserServiceOp) CommentsOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Comments, *Response, error) {
func (s *UserService) CommentsOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Comments, *Response, error) {
form := newSearchOptions(opts...)
path := fmt.Sprintf("user/%s/comments", username)
@ -263,7 +225,7 @@ func (s *UserServiceOp) CommentsOf(ctx context.Context, username string, opts ..
}
// Saved returns a list of the user's saved posts and comments.
func (s *UserServiceOp) Saved(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error) {
func (s *UserService) Saved(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error) {
form := newSearchOptions(opts...)
path := fmt.Sprintf("user/%s/saved", s.client.Username)
@ -284,7 +246,7 @@ func (s *UserServiceOp) Saved(ctx context.Context, opts ...SearchOptionSetter) (
}
// Upvoted returns a list of the user's upvoted posts.
func (s *UserServiceOp) Upvoted(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) {
func (s *UserService) Upvoted(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) {
form := newSearchOptions(opts...)
path := fmt.Sprintf("user/%s/upvoted", s.client.Username)
@ -305,7 +267,7 @@ func (s *UserServiceOp) Upvoted(ctx context.Context, opts ...SearchOptionSetter)
}
// Downvoted returns a list of the user's downvoted posts.
func (s *UserServiceOp) Downvoted(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) {
func (s *UserService) Downvoted(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) {
form := newSearchOptions(opts...)
path := fmt.Sprintf("user/%s/downvoted", s.client.Username)
@ -326,7 +288,7 @@ func (s *UserServiceOp) Downvoted(ctx context.Context, opts ...SearchOptionSette
}
// Hidden returns a list of the user's hidden posts.
func (s *UserServiceOp) Hidden(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) {
func (s *UserService) Hidden(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) {
form := newSearchOptions(opts...)
path := fmt.Sprintf("user/%s/hidden", s.client.Username)
@ -347,7 +309,7 @@ func (s *UserServiceOp) Hidden(ctx context.Context, opts ...SearchOptionSetter)
}
// Gilded returns a list of the user's gilded posts.
func (s *UserServiceOp) Gilded(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) {
func (s *UserService) Gilded(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) {
form := newSearchOptions(opts...)
path := fmt.Sprintf("user/%s/gilded", s.client.Username)
@ -369,7 +331,7 @@ func (s *UserServiceOp) Gilded(ctx context.Context, opts ...SearchOptionSetter)
// GetFriendship returns friendship details with the specified user.
// If the user is not your friend, it will return an error.
func (s *UserServiceOp) GetFriendship(ctx context.Context, username string) (*Friendship, *Response, error) {
func (s *UserService) GetFriendship(ctx context.Context, username string) (*Friendship, *Response, error) {
path := fmt.Sprintf("api/v1/me/friends/%s", username)
req, err := s.client.NewRequest(http.MethodGet, path, nil)
@ -387,7 +349,7 @@ func (s *UserServiceOp) GetFriendship(ctx context.Context, username string) (*Fr
}
// Friend friends a user.
func (s *UserServiceOp) Friend(ctx context.Context, username string) (*Friendship, *Response, error) {
func (s *UserService) Friend(ctx context.Context, username string) (*Friendship, *Response, error) {
type request struct {
Username string `json:"name"`
}
@ -410,7 +372,7 @@ func (s *UserServiceOp) Friend(ctx context.Context, username string) (*Friendshi
}
// Unfriend unfriends a user.
func (s *UserServiceOp) Unfriend(ctx context.Context, username string) (*Response, error) {
func (s *UserService) Unfriend(ctx context.Context, username string) (*Response, error) {
path := fmt.Sprintf("api/v1/me/friends/%s", username)
req, err := s.client.NewRequest(http.MethodDelete, path, nil)
if err != nil {
@ -420,7 +382,7 @@ func (s *UserServiceOp) Unfriend(ctx context.Context, username string) (*Respons
}
// Block blocks a user.
func (s *UserServiceOp) Block(ctx context.Context, username string) (*Blocked, *Response, error) {
func (s *UserService) Block(ctx context.Context, username string) (*Blocked, *Response, error) {
path := "api/block_user"
form := url.Values{}
@ -441,7 +403,7 @@ func (s *UserServiceOp) Block(ctx context.Context, username string) (*Blocked, *
}
// // BlockByID blocks a user via their full id.
// func (s *UserServiceOp) BlockByID(ctx context.Context, id string) (*Blocked, *Response, error) {
// func (s *UserService) BlockByID(ctx context.Context, id string) (*Blocked, *Response, error) {
// path := "api/block_user"
// form := url.Values{}
@ -462,7 +424,7 @@ func (s *UserServiceOp) Block(ctx context.Context, username string) (*Blocked, *
// }
// Unblock unblocks a user.
func (s *UserServiceOp) Unblock(ctx context.Context, username string) (*Response, error) {
func (s *UserService) Unblock(ctx context.Context, username string) (*Response, error) {
selfID, err := s.client.GetRedditID(ctx)
if err != nil {
return nil, err
@ -484,7 +446,7 @@ func (s *UserServiceOp) Unblock(ctx context.Context, username string) (*Response
}
// // UnblockByID unblocks a user via their full id.
// func (s *UserServiceOp) UnblockByID(ctx context.Context, id string) (*Response, error) {
// func (s *UserService) UnblockByID(ctx context.Context, id string) (*Response, error) {
// selfID, err := s.client.GetRedditID(ctx)
// if err != nil {
// return nil, err
@ -506,12 +468,12 @@ func (s *UserServiceOp) Unblock(ctx context.Context, username string) (*Response
// }
// Trophies returns a list of your trophies.
func (s *UserServiceOp) Trophies(ctx context.Context) ([]Trophy, *Response, error) {
func (s *UserService) Trophies(ctx context.Context) ([]Trophy, *Response, error) {
return s.TrophiesOf(ctx, s.client.Username)
}
// TrophiesOf returns a list of the specified user's trophies.
func (s *UserServiceOp) TrophiesOf(ctx context.Context, username string) ([]Trophy, *Response, error) {
func (s *UserService) TrophiesOf(ctx context.Context, username string) ([]Trophy, *Response, error) {
path := fmt.Sprintf("api/v1/user/%s/trophies", username)
req, err := s.client.NewRequest(http.MethodGet, path, nil)

View file

@ -205,8 +205,6 @@ func TestUserService_Overview(t *testing.T) {
posts, comments, _, err := client.User.Overview(ctx)
assert.NoError(t, err)
assert.NotNil(t, posts)
assert.NotNil(t, comments)
assert.Len(t, posts.Posts, 1)
assert.Equal(t, expectedPost, posts.Posts[0])
@ -232,8 +230,6 @@ func TestUserService_OverviewOf(t *testing.T) {
posts, comments, _, err := client.User.OverviewOf(ctx, "user2")
assert.NoError(t, err)
assert.NotNil(t, posts)
assert.NotNil(t, comments)
assert.Len(t, posts.Posts, 1)
assert.Equal(t, expectedPost, posts.Posts[0])
@ -284,7 +280,6 @@ func TestUserService_Posts(t *testing.T) {
posts, _, err := client.User.Posts(ctx)
assert.NoError(t, err)
assert.NotNil(t, posts)
assert.Len(t, posts.Posts, 1)
assert.Equal(t, expectedPost, posts.Posts[0])
@ -305,7 +300,6 @@ func TestUserService_PostsOf(t *testing.T) {
posts, _, err := client.User.PostsOf(ctx, "user2")
assert.NoError(t, err)
assert.NotNil(t, posts)
assert.Len(t, posts.Posts, 1)
assert.Equal(t, expectedPost, posts.Posts[0])
@ -350,7 +344,6 @@ func TestUserService_Comments(t *testing.T) {
comments, _, err := client.User.Comments(ctx)
assert.NoError(t, err)
assert.NotNil(t, comments)
assert.Len(t, comments.Comments, 1)
assert.Equal(t, expectedComment, comments.Comments[0])
@ -371,7 +364,6 @@ func TestUserService_CommentsOf(t *testing.T) {
comments, _, err := client.User.CommentsOf(ctx, "user2")
assert.NoError(t, err)
assert.NotNil(t, comments)
assert.Len(t, comments.Comments, 1)
assert.Equal(t, expectedComment, comments.Comments[0])
@ -417,8 +409,6 @@ func TestUserService_Saved(t *testing.T) {
posts, comments, _, err := client.User.Saved(ctx)
assert.NoError(t, err)
assert.NotNil(t, posts)
assert.NotNil(t, comments)
assert.Len(t, posts.Posts, 1)
assert.Equal(t, expectedPost, posts.Posts[0])
@ -469,7 +459,6 @@ func TestUserService_Upvoted(t *testing.T) {
posts, _, err := client.User.Upvoted(ctx)
assert.NoError(t, err)
assert.NotNil(t, posts)
assert.Len(t, posts.Posts, 1)
assert.Equal(t, expectedPost, posts.Posts[0])
@ -515,7 +504,6 @@ func TestUserService_Downvoted(t *testing.T) {
posts, _, err := client.User.Downvoted(ctx)
assert.NoError(t, err)
assert.NotNil(t, posts)
assert.Len(t, posts.Posts, 1)
assert.Equal(t, expectedPost, posts.Posts[0])
@ -562,7 +550,6 @@ func TestUserService_Hidden(t *testing.T) {
posts, _, err := client.User.Hidden(ctx)
assert.NoError(t, err)
assert.NotNil(t, posts)
assert.Len(t, posts.Posts, 1)
assert.Equal(t, expectedPost, posts.Posts[0])
@ -584,7 +571,6 @@ func TestUserService_Gilded(t *testing.T) {
posts, _, err := client.User.Gilded(ctx)
assert.NoError(t, err)
assert.NotNil(t, posts)
assert.Len(t, posts.Posts, 1)
assert.Equal(t, expectedPost, posts.Posts[0])

23
vote.go
View file

@ -8,18 +8,9 @@ import (
// VoteService handles communication with the upvote/downvote
// related methods of the Reddit API.
type VoteService interface {
Up(ctx context.Context, id string) (*Response, error)
Down(ctx context.Context, id string) (*Response, error)
Remove(ctx context.Context, id string) (*Response, error)
}
// VoteServiceOp implements the Vote interface.
type VoteServiceOp struct {
client *Client
}
var _ VoteService = &VoteServiceOp{}
//
// Reddit API docs: https://www.reddit.com/dev/api/#POST_api_vote
type VoteService service
type vote int
@ -30,7 +21,7 @@ const (
upvote
)
func (s *VoteServiceOp) vote(ctx context.Context, id string, vote vote) (*Response, error) {
func (s *VoteService) vote(ctx context.Context, id string, vote vote) (*Response, error) {
path := "api/vote"
form := url.Values{}
@ -47,16 +38,16 @@ func (s *VoteServiceOp) vote(ctx context.Context, id string, vote vote) (*Respon
}
// Up upvotes a post or a comment.
func (s *VoteServiceOp) Up(ctx context.Context, id string) (*Response, error) {
func (s *VoteService) Up(ctx context.Context, id string) (*Response, error) {
return s.vote(ctx, id, upvote)
}
// Down downvotes a post or a comment.
func (s *VoteServiceOp) Down(ctx context.Context, id string) (*Response, error) {
func (s *VoteService) Down(ctx context.Context, id string) (*Response, error) {
return s.vote(ctx, id, downvote)
}
// Remove removes the user's vote on a post or a comment.
func (s *VoteServiceOp) Remove(ctx context.Context, id string) (*Response, error) {
func (s *VoteService) Remove(ctx context.Context, id string) (*Response, error) {
return s.vote(ctx, id, novote)
}

View file

@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/assert"
)
func TestVoteServiceOp_Up(t *testing.T) {
func TestVoteService_Up(t *testing.T) {
setup()
defer teardown()
@ -24,8 +24,6 @@ func TestVoteServiceOp_Up(t *testing.T) {
err := r.ParseForm()
assert.NoError(t, err)
assert.Equal(t, form, r.PostForm)
fmt.Fprint(w, `{}`)
})
res, err := client.Vote.Up(ctx, "t3_test")
@ -33,7 +31,7 @@ func TestVoteServiceOp_Up(t *testing.T) {
assert.Equal(t, http.StatusOK, res.StatusCode)
}
func TestVoteServiceOp_Down(t *testing.T) {
func TestVoteService_Down(t *testing.T) {
setup()
defer teardown()
@ -48,8 +46,6 @@ func TestVoteServiceOp_Down(t *testing.T) {
err := r.ParseForm()
assert.NoError(t, err)
assert.Equal(t, form, r.PostForm)
fmt.Fprint(w, `{}`)
})
res, err := client.Vote.Down(ctx, "t3_test")
@ -57,7 +53,7 @@ func TestVoteServiceOp_Down(t *testing.T) {
assert.Equal(t, http.StatusOK, res.StatusCode)
}
func TestVoteServiceOp_Remove(t *testing.T) {
func TestVoteService_Remove(t *testing.T) {
setup()
defer teardown()
@ -72,8 +68,6 @@ func TestVoteServiceOp_Remove(t *testing.T) {
err := r.ParseForm()
assert.NoError(t, err)
assert.Equal(t, form, r.PostForm)
fmt.Fprint(w, `{}`)
})
res, err := client.Vote.Remove(ctx, "t3_test")