diff --git a/examples/client-on-request-completed/main.go b/examples/client-on-request-completed/main.go index 0ce8ba2..7f814cf 100644 --- a/examples/client-on-request-completed/main.go +++ b/examples/client-on-request-completed/main.go @@ -27,10 +27,16 @@ func run() (err error) { client.OnRequestCompleted(logResponse) - client.Subreddit.Search(ctx, "programming", reddit.SetLimit(10)) + client.Subreddit.Search(ctx, "programming", nil) client.Subreddit.SearchNames(ctx, "monitor") - client.Subreddit.SearchPosts(ctx, "react", "webdev", reddit.SortByNumberOfComments) - client.User.Posts(ctx, reddit.SetLimit(50)) + client.Subreddit.SearchPosts(ctx, "react", "webdev", nil) + client.User.Posts(ctx, &reddit.ListUserOverviewOptions{ + ListOptions: reddit.ListOptions{ + Limit: 50, + }, + Sort: "top", + Time: "month", + }) return } diff --git a/examples/get-top-posts/main.go b/examples/get-top-posts/main.go index 1bc130a..e11a017 100644 --- a/examples/get-top-posts/main.go +++ b/examples/get-top-posts/main.go @@ -27,7 +27,12 @@ func run() (err error) { // Let's get the top 200 posts of r/golang. // Reddit returns a maximum of 100 posts at a time, // so we'll need to separate this into 2 requests. - result, _, err := client.Subreddit.TopPosts(ctx, "golang", reddit.SetLimit(100), reddit.FromAllTime) + result, _, err := client.Subreddit.TopPosts(ctx, "golang", &reddit.ListPostOptions{ + ListOptions: reddit.ListOptions{ + Limit: 100, + }, + Time: "all", + }) if err != nil { return } @@ -38,7 +43,13 @@ func run() (err error) { // The SetAfter option sets the id of an item that Reddit // will use as an anchor point for the returned listing. - result, _, err = client.Subreddit.TopPosts(ctx, "golang", reddit.SetLimit(100), reddit.FromAllTime, reddit.SetAfter(result.After)) + result, _, err = client.Subreddit.TopPosts(ctx, "golang", &reddit.ListPostOptions{ + ListOptions: reddit.ListOptions{ + Limit: 100, + After: result.After, + }, + Time: "all", + }) if err != nil { return } diff --git a/moderation.go b/moderation.go index 74e2c70..1a52a3f 100644 --- a/moderation.go +++ b/moderation.go @@ -41,11 +41,36 @@ type ModAction struct { // GetActions gets a list of moderator actions on a subreddit. // By default, the limit parameter is 25; max is 500. -func (s *ModerationService) GetActions(ctx context.Context, subreddit string, opts ...SearchOptionSetter) (*ModActions, *Response, error) { - form := newSearchOptions(opts...) +func (s *ModerationService) GetActions(ctx context.Context, subreddit string, opts *ListOptions) (*ModActions, *Response, error) { + return s.GetActionsByType(ctx, subreddit, "", opts) +} +// GetActionsByType gets a list of moderator actions of the specified type on a subreddit. +// By default, the limit parameter is 25; max is 500. +// The type should be one of: banuser, unbanuser, spamlink, removelink, approvelink, spamcomment, +// removecomment, approvecomment, addmoderator, showcomment, invitemoderator, uninvitemoderator, +// acceptmoderatorinvite, removemoderator, addcontributor, removecontributor, editsettings, editflair, +// distinguish, marknsfw, wikibanned, wikicontributor, wikiunbanned, wikipagelisted, removewikicontributor, +// wikirevise, wikipermlevel, ignorereports, unignorereports, setpermissions, setsuggestedsort, sticky, +// unsticky, setcontestmode, unsetcontestmode, lock, unlock, muteuser, unmuteuser, createrule, editrule, +// reorderrules, deleterule, spoiler, unspoiler, modmail_enrollment, community_styling, community_widgets, +// markoriginalcontent, collections, events, hidden_award, add_community_topics, remove_community_topics, +// create_scheduled_post, edit_scheduled_post, delete_scheduled_post, submit_scheduled_post, edit_post_requirements, +// invitesubscriber, submit_content_rating_survey. +func (s *ModerationService) GetActionsByType(ctx context.Context, subreddit string, actionType string, opts *ListOptions) (*ModActions, *Response, error) { path := fmt.Sprintf("r/%s/about/log", subreddit) - path = addQuery(path, form) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } + + type params struct { + Type string `url:"type,omitempty"` + } + path, err = addOptions(path, params{actionType}) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -61,22 +86,6 @@ func (s *ModerationService) GetActions(ctx context.Context, subreddit string, op return root.getModeratorActions(), resp, nil } -// GetActionsByType gets a list of moderator actions of the specified type on a subreddit. -// By default, the limit parameter is 25; max is 500. -// The type should be one of: banuser, unbanuser, spamlink, removelink, approvelink, spamcomment, -// removecomment, approvecomment, addmoderator, showcomment, invitemoderator, uninvitemoderator, -// acceptmoderatorinvite, removemoderator, addcontributor, removecontributor, editsettings, editflair, -// distinguish, marknsfw, wikibanned, wikicontributor, wikiunbanned, wikipagelisted, removewikicontributor, -// wikirevise, wikipermlevel, ignorereports, unignorereports, setpermissions, setsuggestedsort, sticky, -// unsticky, setcontestmode, unsetcontestmode, lock, unlock, muteuser, unmuteuser, createrule, editrule, -// reorderrules, deleterule, spoiler, unspoiler, modmail_enrollment, community_styling, community_widgets, -// markoriginalcontent, collections, events, hidden_award, add_community_topics, remove_community_topics, -// create_scheduled_post, edit_scheduled_post, delete_scheduled_post, submit_scheduled_post, edit_post_requirements -func (s *ModerationService) GetActionsByType(ctx context.Context, subreddit string, actionType string, opts ...SearchOptionSetter) (*ModActions, *Response, error) { - opts = append(opts, setType(actionType)) - return s.GetActions(ctx, subreddit, opts...) -} - // AcceptInvite accepts a pending invite to moderate the specified subreddit. func (s *ModerationService) AcceptInvite(ctx context.Context, subreddit string) (*Response, error) { path := fmt.Sprintf("r/%s/api/accept_moderator_invite", subreddit) diff --git a/moderation_test.go b/moderation_test.go index 4361583..7685f54 100644 --- a/moderation_test.go +++ b/moderation_test.go @@ -61,7 +61,7 @@ func TestModerationService_GetActions(t *testing.T) { fmt.Fprint(w, blob) }) - result, _, err := client.Moderation.GetActions(ctx, "testsubreddit") + result, _, err := client.Moderation.GetActions(ctx, "testsubreddit", nil) assert.NoError(t, err) assert.Equal(t, expectedModActionsResult, result) } @@ -86,7 +86,7 @@ func TestModerationService_GetActionsByType(t *testing.T) { fmt.Fprint(w, blob) }) - result, _, err := client.Moderation.GetActionsByType(ctx, "testsubreddit", "testtype") + result, _, err := client.Moderation.GetActionsByType(ctx, "testsubreddit", "testtype", nil) assert.NoError(t, err) assert.Equal(t, expectedModActionsResult, result) } diff --git a/reddit.go b/reddit.go index 7600c42..376840d 100644 --- a/reddit.go +++ b/reddit.go @@ -360,6 +360,68 @@ type ListOptions struct { Before string `url:"before,omitempty"` } +// Value returns the value stored in the pointer. +func (o *ListOptions) Value() ListOptions { + if o == nil { + return ListOptions{} + } + return *o +} + +// GetLimit returns the limit option. +func (o *ListOptions) GetLimit() int { + if o == nil { + return 0 + } + return o.Limit +} + +// GetAfter returns the after option. +func (o *ListOptions) GetAfter() string { + if o == nil { + return "" + } + return o.After +} + +// GetBefore returns the before option. +func (o *ListOptions) GetBefore() string { + if o == nil { + return "" + } + return o.Before +} + +// ListSubredditOptions defines possible options used when searching for subreddits. +type ListSubredditOptions struct { + ListOptions + // One of: relevance, activity. + Sort string `url:"sort,omitempty"` +} + +// ListPostOptions defines possible options used when getting posts from a subreddit. +type ListPostOptions struct { + ListOptions + // One of: hour, day, week, month, year, all. + Time string `url:"t,omitempty"` +} + +// ListPostSearchOptions defines possible options used when searching for posts within a subreddit. +type ListPostSearchOptions struct { + ListPostOptions + // One of: relevance, hot, top, new, comments. + Sort string `url:"sort,omitempty"` +} + +// ListUserOverviewOptions defines possible options used when getting a user's post and/or comments. +type ListUserOverviewOptions struct { + ListOptions + // One of: hot, new, top, controversial. + Sort string `url:"sort,omitempty"` + // One of: hour, day, week, month, year, all. + Time string `url:"t,omitempty"` +} + func addOptions(s string, opt interface{}) (string, error) { v := reflect.ValueOf(opt) if v.Kind() == reflect.Ptr && v.IsNil() { diff --git a/reddit_oauth.go b/reddit_oauth.go index a2783cd..ef440fb 100644 --- a/reddit_oauth.go +++ b/reddit_oauth.go @@ -60,6 +60,7 @@ type oauth2Config struct { } func oauth2Transport(c oauth2Config) *oauth2.Transport { + // todo: use oauth2.Config.PasswordCredentialsToken params := url.Values{ "grant_type": {"password"}, "username": {c.username}, diff --git a/search.go b/search.go deleted file mode 100644 index 26a250d..0000000 --- a/search.go +++ /dev/null @@ -1,148 +0,0 @@ -package reddit - -import ( - "fmt" - "net/url" -) - -/* - -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, e.g. sometimes limit=1 returns nothing when it should. - -*/ - -func newSearchOptions(opts ...SearchOptionSetter) url.Values { - searchOptions := make(url.Values) - for _, opt := range opts { - opt(searchOptions) - } - return searchOptions -} - -// SearchOptionSetter sets values for the options. -type SearchOptionSetter func(opts url.Values) - -// SetAfter sets the after option. -func SetAfter(v string) SearchOptionSetter { - return func(opts url.Values) { - opts.Set("after", v) - } -} - -// SetBefore sets the before option. -func SetBefore(v string) SearchOptionSetter { - return func(opts url.Values) { - opts.Set("before", v) - } -} - -// SetLimit sets the limit option. -// Warning: It seems like setting the limit to 1 sometimes returns 0 results. -func SetLimit(v int) SearchOptionSetter { - return func(opts url.Values) { - opts.Set("limit", fmt.Sprint(v)) - } -} - -// SortByHot sets the sort option to return the hottest results first. -func SortByHot(opts url.Values) { - opts.Set("sort", "hot") -} - -// SortByBest sets the sort option to return the best results first. -func SortByBest(opts url.Values) { - opts.Set("sort", "best") -} - -// SortByNew sets the sort option to return the newest results first. -func SortByNew(opts url.Values) { - opts.Set("sort", "new") -} - -// SortByRising sets the sort option to return the rising results first. -func SortByRising(opts url.Values) { - opts.Set("sort", "rising") -} - -// SortByControversial sets the sort option to return the most controversial results first. -func SortByControversial(opts url.Values) { - opts.Set("sort", "controversial") -} - -// SortByTop sets the sort option to return the top results first. -func SortByTop(opts url.Values) { - opts.Set("sort", "top") -} - -// SortByRelevance sets the sort option to return the most relevant results first. -// This can be used when searching for subreddits and users. -func SortByRelevance(opts url.Values) { - opts.Set("sort", "relevance") -} - -// SortByActivity sets the sort option to return results with the most activity first. -// This can be used when searching for subreddits and users. -func SortByActivity(opts url.Values) { - opts.Set("sort", "activity") -} - -// SortByNumberOfComments sets the sort option to return the results with the highest -// number of comments first. -// This can be used when searching for posts within a subreddit. -func SortByNumberOfComments(opts url.Values) { - opts.Set("sort", "comments") -} - -// FromThePastHour sets the timespan option to return results from the past hour. -func FromThePastHour(opts url.Values) { - opts.Set("t", "hour") -} - -// FromThePastDay sets the timespan option to return results from the past day. -func FromThePastDay(opts url.Values) { - opts.Set("t", "day") -} - -// FromThePastWeek sets the timespan option to return results from the past week. -func FromThePastWeek(opts url.Values) { - opts.Set("t", "week") -} - -// FromThePastMonth sets the timespan option to return results from the past month. -func FromThePastMonth(opts url.Values) { - opts.Set("t", "month") -} - -// FromThePastYear sets the timespan option to return results from the past year. -func FromThePastYear(opts url.Values) { - opts.Set("t", "year") -} - -// FromAllTime sets the timespan option to return results from all time. -func FromAllTime(opts url.Values) { - opts.Set("t", "all") -} - -// setType sets the type option. -// For mod actions, it's for the type of action (e.g. "banuser", "spamcomment"). -func setType(v string) SearchOptionSetter { - return func(opts url.Values) { - opts.Set("type", v) - } -} - -// setQuery sets the q option. -func setQuery(v string) SearchOptionSetter { - return func(opts url.Values) { - opts.Set("q", v) - } -} - -// setRestrict sets the restrict_sr option. -func setRestrict(opts url.Values) { - opts.Set("restrict_sr", "true") -} diff --git a/subreddit.go b/subreddit.go index 8367e68..bad97a2 100644 --- a/subreddit.go +++ b/subreddit.go @@ -40,14 +40,16 @@ type Moderator struct { Permissions []string `json:"mod_permissions"` } -func (s *SubredditService) getPosts(ctx context.Context, sort string, subreddit string, opts ...SearchOptionSetter) (*Posts, *Response, error) { +func (s *SubredditService) getPosts(ctx context.Context, sort string, subreddit string, opts *ListPostOptions) (*Posts, *Response, error) { path := sort if subreddit != "" { path = fmt.Sprintf("r/%s/%s", subreddit, sort) } - form := newSearchOptions(opts...) - path = addQuery(path, form) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -68,36 +70,36 @@ func (s *SubredditService) getPosts(ctx context.Context, sort string, subreddit // If none are defined, it returns the ones from your subscribed subreddits. // Note: when looking for hot posts in a subreddit, it will include the stickied // posts (if any) PLUS posts from the limit parameter (25 by default). -func (s *SubredditService) HotPosts(ctx context.Context, subreddit string, opts ...SearchOptionSetter) (*Posts, *Response, error) { - return s.getPosts(ctx, "hot", subreddit, opts...) +func (s *SubredditService) HotPosts(ctx context.Context, subreddit string, opts *ListPostOptions) (*Posts, *Response, error) { + return s.getPosts(ctx, "hot", subreddit, opts) } // NewPosts returns the newest posts from the specified subreddit. // To search through multiple, separate the names with a plus (+), e.g. "golang+test". // If none are defined, it returns the ones from your subscribed subreddits. -func (s *SubredditService) NewPosts(ctx context.Context, subreddit string, opts ...SearchOptionSetter) (*Posts, *Response, error) { - return s.getPosts(ctx, "new", subreddit, opts...) +func (s *SubredditService) NewPosts(ctx context.Context, subreddit string, opts *ListPostOptions) (*Posts, *Response, error) { + return s.getPosts(ctx, "new", subreddit, opts) } // RisingPosts returns the rising posts from the specified subreddit. // To search through multiple, separate the names with a plus (+), e.g. "golang+test". // If none are defined, it returns the ones from your subscribed subreddits. -func (s *SubredditService) RisingPosts(ctx context.Context, subreddit string, opts ...SearchOptionSetter) (*Posts, *Response, error) { - return s.getPosts(ctx, "rising", subreddit, opts...) +func (s *SubredditService) RisingPosts(ctx context.Context, subreddit string, opts *ListPostOptions) (*Posts, *Response, error) { + return s.getPosts(ctx, "rising", subreddit, opts) } // ControversialPosts returns the most controversial posts from the specified subreddit. // To search through multiple, separate the names with a plus (+), e.g. "golang+test". // If none are defined, it returns the ones from your subscribed subreddits. -func (s *SubredditService) ControversialPosts(ctx context.Context, subreddit string, opts ...SearchOptionSetter) (*Posts, *Response, error) { - return s.getPosts(ctx, "controversial", subreddit, opts...) +func (s *SubredditService) ControversialPosts(ctx context.Context, subreddit string, opts *ListPostOptions) (*Posts, *Response, error) { + return s.getPosts(ctx, "controversial", subreddit, opts) } // TopPosts returns the top posts from the specified subreddit. // To search through multiple, separate the names with a plus (+), e.g. "golang+test". // If none are defined, it returns the ones from your subscribed subreddits. -func (s *SubredditService) TopPosts(ctx context.Context, subreddit string, opts ...SearchOptionSetter) (*Posts, *Response, error) { - return s.getPosts(ctx, "top", subreddit, opts...) +func (s *SubredditService) TopPosts(ctx context.Context, subreddit string, opts *ListPostOptions) (*Posts, *Response, error) { + return s.getPosts(ctx, "top", subreddit, opts) } // Get gets a subreddit by name. @@ -122,38 +124,38 @@ func (s *SubredditService) Get(ctx context.Context, name string) (*Subreddit, *R } // Popular returns popular subreddits. -func (s *SubredditService) Popular(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) { +func (s *SubredditService) Popular(ctx context.Context, opts *ListSubredditOptions) (*Subreddits, *Response, error) { return s.getSubreddits(ctx, "subreddits/popular", opts) } // New returns new subreddits. -func (s *SubredditService) New(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) { +func (s *SubredditService) New(ctx context.Context, opts *ListSubredditOptions) (*Subreddits, *Response, error) { return s.getSubreddits(ctx, "subreddits/new", opts) } // Gold returns gold subreddits (i.e. only accessible to users with gold). // It seems like it returns an empty list if you don't have gold. -func (s *SubredditService) Gold(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) { +func (s *SubredditService) Gold(ctx context.Context, opts *ListSubredditOptions) (*Subreddits, *Response, error) { return s.getSubreddits(ctx, "subreddits/gold", opts) } // Default returns default subreddits. -func (s *SubredditService) Default(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) { +func (s *SubredditService) Default(ctx context.Context, opts *ListSubredditOptions) (*Subreddits, *Response, error) { return s.getSubreddits(ctx, "subreddits/default", opts) } // Subscribed returns the list of subreddits you are subscribed to. -func (s *SubredditService) Subscribed(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) { +func (s *SubredditService) Subscribed(ctx context.Context, opts *ListSubredditOptions) (*Subreddits, *Response, error) { return s.getSubreddits(ctx, "subreddits/mine/subscriber", opts) } // Approved returns the list of subreddits you are an approved user in. -func (s *SubredditService) Approved(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) { +func (s *SubredditService) Approved(ctx context.Context, opts *ListSubredditOptions) (*Subreddits, *Response, error) { return s.getSubreddits(ctx, "subreddits/mine/contributor", opts) } // Moderated returns the list of subreddits you are a moderator of. -func (s *SubredditService) Moderated(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) { +func (s *SubredditService) Moderated(ctx context.Context, opts *ListSubredditOptions) (*Subreddits, *Response, error) { return s.getSubreddits(ctx, "subreddits/mine/moderator", opts) } @@ -211,11 +213,21 @@ func (s *SubredditService) UnsubscribeByID(ctx context.Context, ids ...string) ( } // Search searches for subreddits. -func (s *SubredditService) Search(ctx context.Context, query string, opts ...SearchOptionSetter) (*Subreddits, *Response, error) { - opts = append(opts, setQuery(query)) - form := newSearchOptions(opts...) +func (s *SubredditService) Search(ctx context.Context, query string, opts *ListSubredditOptions) (*Subreddits, *Response, error) { + path := "subreddits/search" + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } + + type params struct { + Query string `url:"q"` + } + path, err = addOptions(path, params{query}) + if err != nil { + return nil, nil, err + } - path := addQuery("subreddits/search", form) req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { return nil, nil, err @@ -251,21 +263,27 @@ func (s *SubredditService) SearchNames(ctx context.Context, query string) ([]str // SearchPosts searches for posts in the specified subreddit. // To search through multiple, separate the names with a plus (+), e.g. "golang+test". // If no subreddit is provided, the search is run against r/all. -func (s *SubredditService) SearchPosts(ctx context.Context, query string, subreddit string, opts ...SearchOptionSetter) (*Posts, *Response, error) { +func (s *SubredditService) SearchPosts(ctx context.Context, query string, subreddit string, opts *ListPostSearchOptions) (*Posts, *Response, error) { if subreddit == "" { subreddit = "all" } - notAll := !strings.EqualFold(subreddit, "all") - if notAll { - opts = append(opts, setRestrict) + path := fmt.Sprintf("r/%s/search", subreddit) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err } - opts = append(opts, setQuery(query)) - form := newSearchOptions(opts...) + type params struct { + Query string `url:"q"` + RestrictSubreddits bool `url:"restrict_sr,omitempty"` + } - path := fmt.Sprintf("r/%s/search", subreddit) - path = addQuery(path, form) + notAll := !strings.EqualFold(subreddit, "all") + path, err = addOptions(path, params{query, notAll}) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -281,7 +299,7 @@ func (s *SubredditService) SearchPosts(ctx context.Context, query string, subred return root.getPosts(), resp, nil } -func (s *SubredditService) getSubreddits(ctx context.Context, path string, opts *ListOptions) (*Subreddits, *Response, error) { +func (s *SubredditService) getSubreddits(ctx context.Context, path string, opts *ListSubredditOptions) (*Subreddits, *Response, error) { path, err := addOptions(path, opts) if err != nil { return nil, nil, err diff --git a/subreddit_test.go b/subreddit_test.go index 9d24df9..73cb2c8 100644 --- a/subreddit_test.go +++ b/subreddit_test.go @@ -219,7 +219,7 @@ var expectedRandomSubreddit = &Subreddit{ Subscribers: 52357, } -func TestSubredditService_Hot(t *testing.T) { +func TestSubredditService_HotPosts(t *testing.T) { setup() defer teardown() @@ -231,12 +231,12 @@ func TestSubredditService_Hot(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.Subreddit.HotPosts(ctx, "test") + posts, _, err := client.Subreddit.HotPosts(ctx, "test", nil) assert.NoError(t, err) assert.Equal(t, expectedPosts, posts) } -func TestSubredditService_New(t *testing.T) { +func TestSubredditService_NewPosts(t *testing.T) { setup() defer teardown() @@ -248,12 +248,12 @@ func TestSubredditService_New(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.Subreddit.NewPosts(ctx, "test") + posts, _, err := client.Subreddit.NewPosts(ctx, "test", nil) assert.NoError(t, err) assert.Equal(t, expectedPosts, posts) } -func TestSubredditService_Rising(t *testing.T) { +func TestSubredditService_RisingPosts(t *testing.T) { setup() defer teardown() @@ -265,12 +265,12 @@ func TestSubredditService_Rising(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.Subreddit.RisingPosts(ctx, "test") + posts, _, err := client.Subreddit.RisingPosts(ctx, "test", nil) assert.NoError(t, err) assert.Equal(t, expectedPosts, posts) } -func TestSubredditService_Controversial(t *testing.T) { +func TestSubredditService_ControversialPosts(t *testing.T) { setup() defer teardown() @@ -282,12 +282,12 @@ func TestSubredditService_Controversial(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.Subreddit.ControversialPosts(ctx, "test") + posts, _, err := client.Subreddit.ControversialPosts(ctx, "test", nil) assert.NoError(t, err) assert.Equal(t, expectedPosts, posts) } -func TestSubredditService_Top(t *testing.T) { +func TestSubredditService_TopPosts(t *testing.T) { setup() defer teardown() @@ -299,7 +299,7 @@ func TestSubredditService_Top(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.Subreddit.TopPosts(ctx, "test") + posts, _, err := client.Subreddit.TopPosts(ctx, "test", nil) assert.NoError(t, err) assert.Equal(t, expectedPosts, posts) } @@ -324,7 +324,7 @@ func TestSubredditService_Get(t *testing.T) { assert.Equal(t, expectedSubreddit, subreddit) } -func TestSubredditService_GetPopular(t *testing.T) { +func TestSubredditService_Popular(t *testing.T) { setup() defer teardown() @@ -341,7 +341,7 @@ func TestSubredditService_GetPopular(t *testing.T) { assert.Equal(t, expectedSubreddits, subreddits) } -func TestSubredditService_GetNew(t *testing.T) { +func TestSubredditService_New(t *testing.T) { setup() defer teardown() @@ -358,7 +358,7 @@ func TestSubredditService_GetNew(t *testing.T) { assert.Equal(t, expectedSubreddits, subreddits) } -func TestSubredditService_GetGold(t *testing.T) { +func TestSubredditService_Gold(t *testing.T) { setup() defer teardown() @@ -375,7 +375,7 @@ func TestSubredditService_GetGold(t *testing.T) { assert.Equal(t, expectedSubreddits, subreddits) } -func TestSubredditService_GetDefault(t *testing.T) { +func TestSubredditService_Default(t *testing.T) { setup() defer teardown() @@ -392,7 +392,7 @@ func TestSubredditService_GetDefault(t *testing.T) { assert.Equal(t, expectedSubreddits, subreddits) } -func TestSubredditService_GetSubscribed(t *testing.T) { +func TestSubredditService_Subscribed(t *testing.T) { setup() defer teardown() @@ -409,7 +409,7 @@ func TestSubredditService_GetSubscribed(t *testing.T) { assert.Equal(t, expectedSubreddits, subreddits) } -func TestSubredditService_GetApproved(t *testing.T) { +func TestSubredditService_Approved(t *testing.T) { setup() defer teardown() @@ -426,7 +426,7 @@ func TestSubredditService_GetApproved(t *testing.T) { assert.Equal(t, expectedSubreddits, subreddits) } -func TestSubredditService_GetModerated(t *testing.T) { +func TestSubredditService_Moderated(t *testing.T) { setup() defer teardown() @@ -579,6 +579,8 @@ func TestSubredditService_Search(t *testing.T) { form := url.Values{} form.Set("q", "golang") + form.Set("limit", "10") + form.Set("sort", "activity") err := r.ParseForm() assert.NoError(t, err) @@ -587,7 +589,12 @@ func TestSubredditService_Search(t *testing.T) { fmt.Fprint(w, blob) }) - subreddits, _, err := client.Subreddit.Search(ctx, "golang") + subreddits, _, err := client.Subreddit.Search(ctx, "golang", &ListSubredditOptions{ + ListOptions: ListOptions{ + Limit: 10, + }, + Sort: "activity", + }) assert.NoError(t, err) assert.Equal(t, expectedSubreddits, subreddits) } @@ -629,7 +636,6 @@ func TestSubredditService_SearchPosts(t *testing.T) { form := url.Values{} form.Set("q", "test") - form.Set("after", "t3_testpost") err := r.ParseForm() assert.NoError(t, err) @@ -638,7 +644,7 @@ func TestSubredditService_SearchPosts(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.Subreddit.SearchPosts(ctx, "test", "", SetAfter("t3_testpost")) + posts, _, err := client.Subreddit.SearchPosts(ctx, "test", "", nil) assert.NoError(t, err) assert.Equal(t, expectedSearchPosts, posts) } @@ -656,7 +662,6 @@ func TestSubredditService_SearchPosts_InSubreddit(t *testing.T) { form := url.Values{} form.Set("q", "test") form.Set("restrict_sr", "true") - form.Set("after", "t3_testpost") err := r.ParseForm() assert.NoError(t, err) @@ -665,7 +670,7 @@ func TestSubredditService_SearchPosts_InSubreddit(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.Subreddit.SearchPosts(ctx, "test", "test", SetAfter("t3_testpost")) + posts, _, err := client.Subreddit.SearchPosts(ctx, "test", "test", nil) assert.NoError(t, err) assert.Equal(t, expectedSearchPosts, posts) } @@ -683,7 +688,6 @@ func TestSubredditService_SearchPosts_InSubreddits(t *testing.T) { form := url.Values{} form.Set("q", "test") form.Set("restrict_sr", "true") - form.Set("after", "t3_testpost") err := r.ParseForm() assert.NoError(t, err) @@ -692,7 +696,7 @@ func TestSubredditService_SearchPosts_InSubreddits(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.Subreddit.SearchPosts(ctx, "test", "test+golang+nba", SetAfter("t3_testpost")) + posts, _, err := client.Subreddit.SearchPosts(ctx, "test", "test+golang+nba", nil) assert.NoError(t, err) assert.Equal(t, expectedSearchPosts, posts) } diff --git a/user.go b/user.go index 143fcef..cfd669a 100644 --- a/user.go +++ b/user.go @@ -154,16 +154,17 @@ func (s *UserService) UsernameAvailable(ctx context.Context, username string) (b } // Overview returns a list of your posts and comments. -func (s *UserService) Overview(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error) { - return s.OverviewOf(ctx, s.client.Username, opts...) +func (s *UserService) Overview(ctx context.Context, opts *ListUserOverviewOptions) (*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 *UserService) OverviewOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error) { - form := newSearchOptions(opts...) - +func (s *UserService) OverviewOf(ctx context.Context, username string, opts *ListUserOverviewOptions) (*Posts, *Comments, *Response, error) { path := fmt.Sprintf("user/%s/overview", username) - path = addQuery(path, form) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -180,16 +181,17 @@ func (s *UserService) OverviewOf(ctx context.Context, username string, opts ...S } // Posts returns a list of your posts. -func (s *UserService) Posts(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) { - return s.PostsOf(ctx, s.client.Username, opts...) +func (s *UserService) Posts(ctx context.Context, opts *ListUserOverviewOptions) (*Posts, *Response, error) { + return s.PostsOf(ctx, s.client.Username, opts) } // PostsOf returns a list of the user's posts. -func (s *UserService) PostsOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Posts, *Response, error) { - form := newSearchOptions(opts...) - +func (s *UserService) PostsOf(ctx context.Context, username string, opts *ListUserOverviewOptions) (*Posts, *Response, error) { path := fmt.Sprintf("user/%s/submitted", username) - path = addQuery(path, form) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -206,16 +208,17 @@ func (s *UserService) PostsOf(ctx context.Context, username string, opts ...Sear } // Comments returns a list of your comments. -func (s *UserService) Comments(ctx context.Context, opts ...SearchOptionSetter) (*Comments, *Response, error) { - return s.CommentsOf(ctx, s.client.Username, opts...) +func (s *UserService) Comments(ctx context.Context, opts *ListUserOverviewOptions) (*Comments, *Response, error) { + return s.CommentsOf(ctx, s.client.Username, opts) } // CommentsOf returns a list of the user's comments. -func (s *UserService) CommentsOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Comments, *Response, error) { - form := newSearchOptions(opts...) - +func (s *UserService) CommentsOf(ctx context.Context, username string, opts *ListUserOverviewOptions) (*Comments, *Response, error) { path := fmt.Sprintf("user/%s/comments", username) - path = addQuery(path, form) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -232,11 +235,12 @@ func (s *UserService) CommentsOf(ctx context.Context, username string, opts ...S } // Saved returns a list of the user's saved posts and comments. -func (s *UserService) Saved(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Comments, *Response, error) { - form := newSearchOptions(opts...) - +func (s *UserService) Saved(ctx context.Context, opts *ListUserOverviewOptions) (*Posts, *Comments, *Response, error) { path := fmt.Sprintf("user/%s/saved", s.client.Username) - path = addQuery(path, form) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -253,17 +257,18 @@ func (s *UserService) Saved(ctx context.Context, opts ...SearchOptionSetter) (*P } // Upvoted returns a list of your upvoted posts. -func (s *UserService) Upvoted(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) { - return s.UpvotedOf(ctx, s.client.Username, opts...) +func (s *UserService) Upvoted(ctx context.Context, opts *ListUserOverviewOptions) (*Posts, *Response, error) { + return s.UpvotedOf(ctx, s.client.Username, opts) } // UpvotedOf returns a list of the user's upvoted posts. -// The user's votes must be public for this to work. -func (s *UserService) UpvotedOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Posts, *Response, error) { - form := newSearchOptions(opts...) - +// The user's votes must be public for this to work (unless the user is you). +func (s *UserService) UpvotedOf(ctx context.Context, username string, opts *ListUserOverviewOptions) (*Posts, *Response, error) { path := fmt.Sprintf("user/%s/upvoted", username) - path = addQuery(path, form) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -280,17 +285,18 @@ func (s *UserService) UpvotedOf(ctx context.Context, username string, opts ...Se } // Downvoted returns a list of your downvoted posts. -func (s *UserService) Downvoted(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) { - return s.DownvotedOf(ctx, s.client.Username, opts...) +func (s *UserService) Downvoted(ctx context.Context, opts *ListUserOverviewOptions) (*Posts, *Response, error) { + return s.DownvotedOf(ctx, s.client.Username, opts) } // DownvotedOf returns a list of the user's downvoted posts. -// The user's votes must be public for this to work. -func (s *UserService) DownvotedOf(ctx context.Context, username string, opts ...SearchOptionSetter) (*Posts, *Response, error) { - form := newSearchOptions(opts...) - +// The user's votes must be public for this to work (unless the user is you). +func (s *UserService) DownvotedOf(ctx context.Context, username string, opts *ListUserOverviewOptions) (*Posts, *Response, error) { path := fmt.Sprintf("user/%s/downvoted", username) - path = addQuery(path, form) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -307,11 +313,12 @@ func (s *UserService) DownvotedOf(ctx context.Context, username string, opts ... } // Hidden returns a list of the user's hidden posts. -func (s *UserService) Hidden(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) { - form := newSearchOptions(opts...) - +func (s *UserService) Hidden(ctx context.Context, opts *ListUserOverviewOptions) (*Posts, *Response, error) { path := fmt.Sprintf("user/%s/hidden", s.client.Username) - path = addQuery(path, form) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -328,11 +335,12 @@ func (s *UserService) Hidden(ctx context.Context, opts ...SearchOptionSetter) (* } // Gilded returns a list of the user's gilded posts. -func (s *UserService) Gilded(ctx context.Context, opts ...SearchOptionSetter) (*Posts, *Response, error) { - form := newSearchOptions(opts...) - +func (s *UserService) Gilded(ctx context.Context, opts *ListUserOverviewOptions) (*Posts, *Response, error) { path := fmt.Sprintf("user/%s/gilded", s.client.Username) - path = addQuery(path, form) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -517,9 +525,12 @@ func (s *UserService) TrophiesOf(ctx context.Context, username string) ([]Trophy } // Popular gets the user subreddits with the most activity. -func (s *UserService) Popular(ctx context.Context, opts ...SearchOptionSetter) (*Subreddits, *Response, error) { - form := newSearchOptions(opts...) - path := addQuery("users/popular", form) +func (s *UserService) Popular(ctx context.Context, opts *ListOptions) (*Subreddits, *Response, error) { + path := "users/popular" + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -536,9 +547,12 @@ func (s *UserService) Popular(ctx context.Context, opts ...SearchOptionSetter) ( } // New gets the most recently created user subreddits. -func (s *UserService) New(ctx context.Context, opts ...SearchOptionSetter) (*Subreddits, *Response, error) { - form := newSearchOptions(opts...) - path := addQuery("users/new", form) +func (s *UserService) New(ctx context.Context, opts *ListUserOverviewOptions) (*Subreddits, *Response, error) { + path := "users/new" + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { @@ -555,11 +569,21 @@ func (s *UserService) New(ctx context.Context, opts ...SearchOptionSetter) (*Sub } // Search searches for users. -func (s *UserService) Search(ctx context.Context, query string, opts ...SearchOptionSetter) (*Users, *Response, error) { - opts = append(opts, setQuery(query)) - form := newSearchOptions(opts...) +// todo: maybe include the sort option? (relevance, activity) +func (s *UserService) Search(ctx context.Context, query string, opts *ListOptions) (*Users, *Response, error) { + path := "users/search" + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } - path := addQuery("users/search", form) + type params struct { + Query string `url:"q"` + } + path, err = addOptions(path, params{query}) + if err != nil { + return nil, nil, err + } req, err := s.client.NewRequest(http.MethodGet, path, nil) if err != nil { diff --git a/user_test.go b/user_test.go index 1e6c80f..a2f7964 100644 --- a/user_test.go +++ b/user_test.go @@ -263,7 +263,7 @@ func TestUserService_Overview(t *testing.T) { fmt.Fprint(w, blob) }) - posts, comments, _, err := client.User.Overview(ctx) + posts, comments, _, err := client.User.Overview(ctx, nil) assert.NoError(t, err) assert.Len(t, posts.Posts, 1) @@ -289,7 +289,7 @@ func TestUserService_OverviewOf(t *testing.T) { fmt.Fprint(w, blob) }) - posts, comments, _, err := client.User.OverviewOf(ctx, "user2") + posts, comments, _, err := client.User.OverviewOf(ctx, "user2", nil) assert.NoError(t, err) assert.Len(t, posts.Posts, 1) @@ -325,7 +325,13 @@ func TestUserService_Overview_Options(t *testing.T) { fmt.Fprint(w, blob) }) - _, _, _, err = client.User.Overview(ctx, SetLimit(5), SetAfter("t3_after"), SortByTop) + _, _, _, err = client.User.Overview(ctx, &ListUserOverviewOptions{ + ListOptions: ListOptions{ + Limit: 5, + After: "t3_after", + }, + Sort: "top", + }) assert.NoError(t, err) } @@ -341,7 +347,7 @@ func TestUserService_Posts(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.User.Posts(ctx) + posts, _, err := client.User.Posts(ctx, nil) assert.NoError(t, err) assert.Len(t, posts.Posts, 1) @@ -362,7 +368,7 @@ func TestUserService_PostsOf(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.User.PostsOf(ctx, "user2") + posts, _, err := client.User.PostsOf(ctx, "user2", nil) assert.NoError(t, err) assert.Len(t, posts.Posts, 1) @@ -392,7 +398,12 @@ func TestUserService_Posts_Options(t *testing.T) { fmt.Fprint(w, blob) }) - _, _, err = client.User.Posts(ctx, SetLimit(10), SortByNew) + _, _, err = client.User.Posts(ctx, &ListUserOverviewOptions{ + ListOptions: ListOptions{ + Limit: 10, + }, + Sort: "new", + }) assert.NoError(t, err) } @@ -408,7 +419,7 @@ func TestUserService_Comments(t *testing.T) { fmt.Fprint(w, blob) }) - comments, _, err := client.User.Comments(ctx) + comments, _, err := client.User.Comments(ctx, nil) assert.NoError(t, err) assert.Len(t, comments.Comments, 1) @@ -429,7 +440,7 @@ func TestUserService_CommentsOf(t *testing.T) { fmt.Fprint(w, blob) }) - comments, _, err := client.User.CommentsOf(ctx, "user2") + comments, _, err := client.User.CommentsOf(ctx, "user2", nil) assert.NoError(t, err) assert.Len(t, comments.Comments, 1) @@ -459,7 +470,12 @@ func TestUserService_Comments_Options(t *testing.T) { fmt.Fprint(w, blob) }) - _, _, err = client.User.Comments(ctx, SetLimit(100), SetBefore("t1_before")) + _, _, err = client.User.Comments(ctx, &ListUserOverviewOptions{ + ListOptions: ListOptions{ + Limit: 100, + Before: "t1_before", + }, + }) assert.NoError(t, err) } @@ -476,7 +492,7 @@ func TestUserService_Saved(t *testing.T) { fmt.Fprint(w, blob) }) - posts, comments, _, err := client.User.Saved(ctx) + posts, comments, _, err := client.User.Saved(ctx, nil) assert.NoError(t, err) assert.Len(t, posts.Posts, 1) @@ -512,7 +528,12 @@ func TestUserService_Saved_Options(t *testing.T) { fmt.Fprint(w, blob) }) - _, _, _, err = client.User.Saved(ctx, SetLimit(50), SortByControversial) + _, _, _, err = client.User.Saved(ctx, &ListUserOverviewOptions{ + ListOptions: ListOptions{ + Limit: 50, + }, + Sort: "controversial", + }) assert.NoError(t, err) } func TestUserService_Upvoted(t *testing.T) { @@ -528,7 +549,7 @@ func TestUserService_Upvoted(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.User.Upvoted(ctx) + posts, _, err := client.User.Upvoted(ctx, nil) assert.NoError(t, err) assert.Len(t, posts.Posts, 1) @@ -559,7 +580,12 @@ func TestUserService_Upvoted_Options(t *testing.T) { fmt.Fprint(w, blob) }) - _, _, err = client.User.Upvoted(ctx, SetLimit(30), SetAfter("t3_after")) + _, _, err = client.User.Upvoted(ctx, &ListUserOverviewOptions{ + ListOptions: ListOptions{ + Limit: 30, + After: "t3_after", + }, + }) assert.NoError(t, err) } @@ -576,7 +602,7 @@ func TestUserService_UpvotedOf(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.User.UpvotedOf(ctx, "user2") + posts, _, err := client.User.UpvotedOf(ctx, "user2", nil) assert.NoError(t, err) assert.Len(t, posts.Posts, 1) @@ -598,7 +624,7 @@ func TestUserService_Downvoted(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.User.Downvoted(ctx) + posts, _, err := client.User.Downvoted(ctx, nil) assert.NoError(t, err) assert.Len(t, posts.Posts, 1) @@ -629,7 +655,12 @@ func TestUserService_Downvoted_Options(t *testing.T) { fmt.Fprint(w, blob) }) - _, _, err = client.User.Downvoted(ctx, SetLimit(20), SetBefore("t3_before")) + _, _, err = client.User.Downvoted(ctx, &ListUserOverviewOptions{ + ListOptions: ListOptions{ + Limit: 20, + Before: "t3_before", + }, + }) assert.NoError(t, err) } @@ -646,7 +677,7 @@ func TestUserService_DownvotedOf(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.User.DownvotedOf(ctx, "user2") + posts, _, err := client.User.DownvotedOf(ctx, "user2", nil) assert.NoError(t, err) assert.Len(t, posts.Posts, 1) @@ -668,7 +699,7 @@ func TestUserService_Hidden(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.User.Hidden(ctx) + posts, _, err := client.User.Hidden(ctx, nil) assert.NoError(t, err) assert.Len(t, posts.Posts, 1) @@ -690,7 +721,7 @@ func TestUserService_Gilded(t *testing.T) { fmt.Fprint(w, blob) }) - posts, _, err := client.User.Gilded(ctx) + posts, _, err := client.User.Gilded(ctx, nil) assert.NoError(t, err) assert.Len(t, posts.Posts, 1) @@ -899,7 +930,7 @@ func TestUserService_Popular(t *testing.T) { fmt.Fprint(w, blob) }) - userSubreddits, _, err := client.User.Popular(ctx) + userSubreddits, _, err := client.User.Popular(ctx, nil) assert.NoError(t, err) assert.Equal(t, expectedUserSubreddits, userSubreddits) } @@ -916,7 +947,7 @@ func TestUserService_New(t *testing.T) { fmt.Fprint(w, blob) }) - userSubreddits, _, err := client.User.New(ctx) + userSubreddits, _, err := client.User.New(ctx, nil) assert.NoError(t, err) assert.Equal(t, expectedUserSubreddits, userSubreddits) } @@ -941,7 +972,7 @@ func TestUserService_Search(t *testing.T) { fmt.Fprint(w, blob) }) - users, _, err := client.User.Search(ctx, "test") + users, _, err := client.User.Search(ctx, "test", nil) assert.NoError(t, err) assert.Equal(t, expectedSearchUsers, users) }