diff --git a/account.go b/account.go index 5cc1a08..738b808 100644 --- a/account.go +++ b/account.go @@ -12,6 +12,7 @@ type AccountService interface { 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. @@ -288,3 +289,26 @@ func (s *AccountServiceOp) UpdateSettings(ctx context.Context, settings *Setting return root, resp, nil } + +// Trophies returns a list of your trophies. +func (s *AccountServiceOp) Trophies(ctx context.Context) ([]Trophy, *Response, error) { + path := "api/v1/me/trophies" + + req, err := s.client.NewRequest(http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + root := new(rootTrophyListing) + resp, err := s.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + + var trophies []Trophy + for _, trophy := range root.Data.Trophies { + trophies = append(trophies, trophy.Data) + } + + return trophies, resp, nil +} diff --git a/account_test.go b/account_test.go index 709830a..f8b83d1 100644 --- a/account_test.go +++ b/account_test.go @@ -167,3 +167,19 @@ func TestAccountServiceOp_UpdateSettings(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expectedSettings, settings) } + +func TestAccountService_Trophies(t *testing.T) { + setup() + defer teardown() + + blob := readFileContents(t, "testdata/account/trophies.json") + + mux.HandleFunc("/api/v1/me/trophies", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method) + fmt.Fprint(w, blob) + }) + + trophies, _, err := client.Account.Trophies(ctx) + assert.NoError(t, err) + assert.Equal(t, expectedTrophies, trophies) +} diff --git a/comment_test.go b/comment_test.go index c2437a1..25c6602 100644 --- a/comment_test.go +++ b/comment_test.go @@ -14,7 +14,7 @@ var expectedCommentSubmitOrEdit = &Comment{ ID: "test2", FullID: "t1_test2", ParentID: "t1_test", - Permalink: "/r/subreddit/comments/test1/some_thread/test2/", + Permalink: "https://www.reddit.com/r/subreddit/comments/test1/some_thread/test2/", Body: "test comment", Author: "reddit_username", diff --git a/subreddit_test.go b/subreddit_test.go index 30147fd..646ba27 100644 --- a/subreddit_test.go +++ b/subreddit_test.go @@ -88,7 +88,7 @@ var expectedSticky = &PostAndComments{ Created: &Timestamp{time.Date(2020, 6, 20, 12, 8, 57, 0, time.UTC)}, Edited: &Timestamp{time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)}, - Permalink: "/r/nba/comments/hcl9gq/daily_discussion_thread_freetalk_and_other/", + Permalink: "https://www.reddit.com/r/nba/comments/hcl9gq/daily_discussion_thread_freetalk_and_other/", URL: "https://www.reddit.com/r/nba/comments/hcl9gq/daily_discussion_thread_freetalk_and_other/", Title: "Daily Discussion Thread | Free-Talk and Other Updates - June 20, 2020", diff --git a/testdata/account/trophies.json b/testdata/account/trophies.json new file mode 100644 index 0000000..88ad1ed --- /dev/null +++ b/testdata/account/trophies.json @@ -0,0 +1,31 @@ +{ + "kind": "TrophyList", + "data": { + "trophies": [ + { + "kind": "t6", + "data": { + "icon_70": "https://www.redditstatic.com/awards2/3_year_club-70.png", + "name": "Three-Year Club", + "url": null, + "icon_40": "https://www.redditstatic.com/awards2/3_year_club-40.png", + "award_id": null, + "id": null, + "description": null + } + }, + { + "kind": "t6", + "data": { + "icon_70": "https://www.redditstatic.com/awards2/verified_email-70.png", + "name": "Verified Email", + "url": null, + "icon_40": "https://www.redditstatic.com/awards2/verified_email-40.png", + "award_id": "o", + "id": "1q1tez", + "description": null + } + } + ] + } +} diff --git a/things.go b/things.go index 77c806d..1a10d7d 100644 --- a/things.go +++ b/things.go @@ -13,8 +13,8 @@ const ( kindSubreddit = "t5" kindAward = "t6" kindListing = "Listing" - kingKarmaList = "KarmaList" - kingTrophyList = "TrophyList" + kindKarmaList = "KarmaList" + kindTrophyList = "TrophyList" kindUserList = "UserList" kindMode = "more" ) @@ -230,10 +230,10 @@ type Comment struct { PostID string `json:"link_id,omitempty"` // These don't appear when submitting a comment - PostTitle string `json:"link_title,omitempty"` - PostPermalink Permalink `json:"link_permalink,omitempty"` - PostAuthor string `json:"link_author,omitempty"` - PostNumComments int `json:"num_comments"` + PostTitle string `json:"link_title,omitempty"` + PostPermalink string `json:"link_permalink,omitempty"` + PostAuthor string `json:"link_author,omitempty"` + PostNumComments int `json:"num_comments"` IsSubmitter bool `json:"is_submitter"` ScoreHidden bool `json:"score_hidden"` diff --git a/user.go b/user.go index ee291bc..123f167 100644 --- a/user.go +++ b/user.go @@ -2,8 +2,6 @@ package geddit import ( "context" - "encoding/json" - "errors" "fmt" "net/http" "net/url" @@ -42,8 +40,8 @@ type UserService interface { Unblock(ctx context.Context, username string) (*Response, error) // UnblockByID(ctx context.Context, id string) (*Response, error) - Trophies(ctx context.Context) (Trophies, *Response, error) - TrophiesOf(ctx context.Context, username string) (Trophies, *Response, error) + Trophies(ctx context.Context) ([]Trophy, *Response, error) + TrophiesOf(ctx context.Context, username string) ([]Trophy, *Response, error) } // UserServiceOp implements the UserService interface. @@ -98,63 +96,22 @@ type Blocked struct { } type rootTrophyListing struct { - Kind string `json:"kind,omitempty"` - Trophies Trophies `json:"data"` + Kind string `json:"kind,omitempty"` + Data struct { + Trophies []rootTrophy `json:"trophies"` + } `json:"data"` +} + +type rootTrophy struct { + Kind string `json:"kind,omitempty"` + Data Trophy `json:"data"` } // Trophy is a Reddit award. type Trophy struct { - Name string `json:"name"` -} - -// Trophies is a list of trophies. -type Trophies []Trophy - -// todo: we don't really need this. just make a rootTrophyListingData struct or something and use the default unmarshaler for that - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (t *Trophies) UnmarshalJSON(b []byte) error { - var data map[string]interface{} - if err := json.Unmarshal(b, &data); err != nil { - return err - } - - trophies, ok := data["trophies"] - if !ok { - return errors.New("data does not contain trophies") - } - - trophyList, ok := trophies.([]interface{}) - if !ok { - return errors.New("unexpected type for list of trophies") - } - - for _, trophyData := range trophyList { - trophyInfo, ok := trophyData.(map[string]interface{}) - if !ok { - continue - } - - info, ok := trophyInfo["data"] - if !ok { - continue - } - - infoBytes, err := json.Marshal(info) - if err != nil { - continue - } - - var trophy Trophy - err = json.Unmarshal(infoBytes, &trophy) - if err != nil { - continue - } - - *t = append(*t, trophy) - } - - return nil + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` } // Get returns information about the user. @@ -549,12 +506,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) (Trophies, *Response, error) { +func (s *UserServiceOp) 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) (Trophies, *Response, error) { +func (s *UserServiceOp) 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) @@ -568,5 +525,10 @@ func (s *UserServiceOp) TrophiesOf(ctx context.Context, username string) (Trophi return nil, resp, err } - return root.Trophies, resp, nil + var trophies []Trophy + for _, trophy := range root.Data.Trophies { + trophies = append(trophies, trophy.Data) + } + + return trophies, resp, nil } diff --git a/user_test.go b/user_test.go index 8a106d0..e2f758b 100644 --- a/user_test.go +++ b/user_test.go @@ -52,7 +52,7 @@ var expectedPost = Post{ Created: &Timestamp{time.Date(2020, 5, 3, 22, 46, 25, 0, time.UTC)}, Edited: &Timestamp{time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)}, - Permalink: "/r/redditdev/comments/gczwql/get_userusernamegilded_does_it_return_other_users/", + Permalink: "https://www.reddit.com/r/redditdev/comments/gczwql/get_userusernamegilded_does_it_return_other_users/", URL: "https://www.reddit.com/r/redditdev/comments/gczwql/get_userusernamegilded_does_it_return_other_users/", Title: "GET /user/{username}/gilded: does it return other user's things you've gilded, or your things that have been gilded? Does it return both comments and posts?", @@ -81,7 +81,7 @@ var expectedComment = Comment{ Edited: &Timestamp{time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)}, ParentID: "t3_d7ejpn", - Permalink: "/r/apple/comments/d7ejpn/im_giving_away_an_iphone_11_pro_to_a_commenter_at/f0zsa37/", + Permalink: "https://www.reddit.com/r/apple/comments/d7ejpn/im_giving_away_an_iphone_11_pro_to_a_commenter_at/f0zsa37/", Body: "Thank you!", Author: "v_95", @@ -116,9 +116,17 @@ var expectedBlocked = &Blocked{ Created: &Timestamp{time.Date(2020, 6, 16, 16, 49, 50, 0, time.UTC)}, } -var expectedTrophies = Trophies{ - {Name: "Three-Year Club"}, - {Name: "Verified Email"}, +var expectedTrophies = []Trophy{ + { + ID: "", + Name: "Three-Year Club", + Description: "", + }, + { + ID: "1q1tez", + Name: "Verified Email", + Description: "", + }, } func TestUserService_Get(t *testing.T) {