diff --git a/collection.go b/collection.go new file mode 100644 index 0000000..c7cfdf5 --- /dev/null +++ b/collection.go @@ -0,0 +1,193 @@ +package reddit + +import ( + "context" + "errors" + "net/http" + "net/url" + "strings" + + "github.com/google/go-querystring/query" +) + +// CollectionService handles communication with the collection +// related methods of the Reddit API. +// +// Reddit API docs: https://www.reddit.com/dev/api/#section_collections +type CollectionService struct { + client *Client +} + +// Collection is a mod curated group of posts within a subreddit. +type Collection struct { + ID string `json:"collection_id,omitempty"` + Created *Timestamp `json:"created_at_utc,omitempty"` + Updated *Timestamp `json:"last_update_utc,omitempty"` + + Title string `json:"title,omitempty"` + Description string `json:"description,omitempty"` + Permalink string `json:"permalink,omitempty"` + Layout string `json:"display_layout,omitempty"` + + SubredditID string `json:"subreddit_id,omitempty"` + Author string `json:"author_name,omitempty"` + AuthorID string `json:"author_id,omitempty"` + + // Post at the top of the collection. + // This does not appear when getting a list of collections. + PrimaryPostID string `json:"primary_link_id,omitempty"` + PostIDs []string `json:"link_ids,omitempty"` +} + +// CollectionCreateRequest represents a request to create a collection. +type CollectionCreateRequest struct { + Title string `url:"title"` + Description string `url:"description,omitempty"` + SubredditID string `url:"sr_fullname"` + // One of: TIMELINE, GALLERY. + Layout string `url:"display_layout,omitempty"` +} + +// Get gets a collection by its ID. +func (s *CollectionService) Get(ctx context.Context, id string) (*Collection, *Response, error) { + path := "api/v1/collections/collection" + + type params struct { + ID string `url:"collection_id"` + IncludePosts bool `url:"include_links"` + } + path, err := addOptions(path, params{id, false}) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + collection := new(Collection) + resp, err := s.client.Do(ctx, req, collection) + if err != nil { + return nil, resp, err + } + + return collection, resp, nil +} + +// FromSubreddit gets all collections in the subreddit. +func (s *CollectionService) FromSubreddit(ctx context.Context, id string) ([]*Collection, *Response, error) { + path := "api/v1/collections/subreddit_collections" + + type params struct { + SubredditID string `url:"sr_fullname"` + } + path, err := addOptions(path, params{id}) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + var collections []*Collection + resp, err := s.client.Do(ctx, req, &collections) + if err != nil { + return nil, resp, err + } + + return collections, resp, nil +} + +// Create creates a collection. +func (s *CollectionService) Create(ctx context.Context, createRequest *CollectionCreateRequest) (*Collection, *Response, error) { + if createRequest == nil { + return nil, nil, errors.New("createRequest: cannot be nil") + } + + path := "api/v1/collections/create_collection" + + // todo: do this for multireddit stuff too + form, err := query.Values(createRequest) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequestWithForm(http.MethodPost, path, form) + if err != nil { + return nil, nil, err + } + + collection := new(Collection) + resp, err := s.client.Do(ctx, req, collection) + if err != nil { + return nil, resp, err + } + + return collection, resp, nil +} + +// Delete deletes a collection via its id. +func (s *CollectionService) Delete(ctx context.Context, id string) (*Response, error) { + path := "api/v1/collections/delete_collection" + + form := url.Values{} + form.Set("collection_id", id) + + req, err := s.client.NewRequestWithForm(http.MethodPost, path, form) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// AddPost adds a post (via its full ID) to a collection (via its id). +func (s *CollectionService) AddPost(ctx context.Context, postID, collectionID string) (*Response, error) { + path := "api/v1/collections/add_post_to_collection" + + form := url.Values{} + form.Set("link_fullname", postID) + form.Set("collection_id", collectionID) + + req, err := s.client.NewRequestWithForm(http.MethodPost, path, form) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// RemovePost removes a post (via its full ID) from a collection (via its id). +func (s *CollectionService) RemovePost(ctx context.Context, postID, collectionID string) (*Response, error) { + path := "api/v1/collections/remove_post_in_collection" + + form := url.Values{} + form.Set("link_fullname", postID) + form.Set("collection_id", collectionID) + + req, err := s.client.NewRequestWithForm(http.MethodPost, path, form) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// ReorderPosts reorders posts in a collection. +func (s *CollectionService) ReorderPosts(ctx context.Context, collectionID string, postIDs ...string) (*Response, error) { + path := "api/v1/collections/reorder_collection" + + form := url.Values{} + form.Set("collection_id", collectionID) + form.Set("link_ids", strings.Join(postIDs, ",")) + + req, err := s.client.NewRequestWithForm(http.MethodPost, path, form) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/reddit.go b/reddit.go index 27f3a30..73f0654 100644 --- a/reddit.go +++ b/reddit.go @@ -92,6 +92,7 @@ type Client struct { redditID string Account *AccountService + Collection *CollectionService Comment *CommentService Flair *FlairService Listings *ListingsService @@ -139,6 +140,7 @@ func newClient(httpClient *http.Client) *Client { c := &Client{client: httpClient, BaseURL: baseURL, TokenURL: tokenURL} c.Account = &AccountService{client: c} + c.Collection = &CollectionService{client: c} c.Flair = &FlairService{client: c} c.Listings = &ListingsService{client: c} c.Moderation = &ModerationService{client: c} diff --git a/reddit_test.go b/reddit_test.go index 2310a7a..29e548b 100644 --- a/reddit_test.go +++ b/reddit_test.go @@ -67,6 +67,7 @@ func readFileContents(path string) (string, error) { func testClientServices(t *testing.T, c *Client) { services := []string{ "Account", + "Collection", "Comment", "Flair", "Listings",