Get/set/delete trusted users. Tweak friends list struct

Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
Vartan Benohanian 2020-07-03 00:51:55 -04:00
parent 12e801f83c
commit 6ffcf70090
5 changed files with 227 additions and 63 deletions

View File

@ -2,9 +2,8 @@ package geddit
import (
"context"
"encoding/json"
"errors"
"net/http"
"net/url"
)
// AccountService handles communication with the account
@ -28,9 +27,10 @@ type SubredditKarma struct {
// Settings are the user's account settings.
// Some of the fields' descriptions are taken from:
// https://praw.readthedocs.io/en/latest/code_overview/other/preferences.html#praw.models.Preferences.update
// todo: these should probably be pointers with omitempty
type Settings struct {
// Control whose private messages you see: "everyone" or "whitelisted".
// Control whose private messages you see.
// - "everyone": everyone except blocked users
// - "whitelisted": only trusted users
AcceptPrivateMessages *string `json:"accept_pms,omitempty"`
// Allow Reddit to use your activity on Reddit to show you more relevant advertisements.
ActivityRelevantAds *bool `json:"activity_relevant_ads,omitempty"`
@ -209,6 +209,13 @@ type Settings struct {
EnableVideoAutoplay *bool `json:"video_autoplay,omitempty"`
}
type rootRelationshipList struct {
Kind string `json:"kind,omitempty"`
Data struct {
Relationships []Relationship `json:"children"`
} `json:"data"`
}
// Info returns some general information about your account.
func (s *AccountService) Info(ctx context.Context) (*User, *Response, error) {
path := "api/v1/me"
@ -296,6 +303,7 @@ func (s *AccountService) Trophies(ctx context.Context) ([]Trophy, *Response, err
return nil, resp, err
}
// todo: use Things struct
var trophies []Trophy
for _, trophy := range root.Data.Trophies {
trophies = append(trophies, trophy.Data)
@ -304,52 +312,6 @@ func (s *AccountService) Trophies(ctx context.Context) ([]Trophy, *Response, err
return trophies, resp, nil
}
type rootFriendList struct {
Friends []Relationship
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (l *rootFriendList) UnmarshalJSON(b []byte) error {
var resBody []interface{}
err := json.Unmarshal(b, &resBody)
if err != nil {
return err
}
if len(resBody) == 0 {
return errors.New("unexpected data length received")
}
data, ok := resBody[0].(map[string]interface{})
if !ok {
return errors.New("unexpected data type received")
}
dataMap, ok := data["data"].(map[string]interface{})
if !ok {
return errors.New("data does not contain expected field")
}
children, ok := dataMap["children"].([]interface{})
if !ok {
return errors.New("data does not contain expected field")
}
byteValue, err := json.Marshal(children)
if err != nil {
return nil
}
var friends []Relationship
err = json.Unmarshal(byteValue, &friends)
if err != nil {
return err
}
l.Friends = friends
return nil
}
// Friends returns a list of your friends.
func (s *AccountService) Friends(ctx context.Context) ([]Relationship, *Response, error) {
path := "prefs/friends"
@ -359,20 +321,13 @@ func (s *AccountService) Friends(ctx context.Context) ([]Relationship, *Response
return nil, nil, err
}
root := new(rootFriendList)
resp, err := s.client.Do(ctx, req, root)
root := make([]rootRelationshipList, 2)
resp, err := s.client.Do(ctx, req, &root)
if err != nil {
return nil, resp, err
}
return root.Friends, resp, nil
}
type rootBlockedListing struct {
Kind string `json:"kind,omitempty"`
Data struct {
Blocked []Relationship `json:"children"`
} `json:"data"`
return root[0].Data.Relationships, resp, nil
}
// Blocked returns a list of your blocked users.
@ -384,11 +339,85 @@ func (s *AccountService) Blocked(ctx context.Context) ([]Relationship, *Response
return nil, nil, err
}
root := new(rootBlockedListing)
root := new(rootRelationshipList)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
return root.Data.Blocked, resp, nil
return root.Data.Relationships, resp, nil
}
// Messaging returns blocked users and trusted users, respectively.
func (s *AccountService) Messaging(ctx context.Context) ([]Relationship, []Relationship, *Response, error) {
path := "prefs/messaging"
req, err := s.client.NewRequest(http.MethodGet, path, nil)
if err != nil {
return nil, nil, nil, err
}
root := make([]rootRelationshipList, 2)
resp, err := s.client.Do(ctx, req, &root)
if err != nil {
return nil, nil, resp, err
}
blocked := root[0].Data.Relationships
trusted := root[1].Data.Relationships
return blocked, trusted, resp, nil
}
// Trusted returns a list of your trusted users.
func (s *AccountService) Trusted(ctx context.Context) ([]Relationship, *Response, error) {
path := "prefs/trusted"
req, err := s.client.NewRequest(http.MethodGet, path, nil)
if err != nil {
return nil, nil, err
}
root := new(rootRelationshipList)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
return root.Data.Relationships, resp, nil
}
// AddTrusted adds a user to your trusted users.
// This is not visible in the Reddit API docs.
func (s *AccountService) AddTrusted(ctx context.Context, username string) (*Response, error) {
path := "api/add_whitelisted"
form := url.Values{}
form.Set("api_type", "json")
form.Set("name", username)
// todo: you can also do this with the user id. form.Set("id", id). should we? or is this enough?
req, err := s.client.NewPostForm(path, form)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// RemoveTrusted removes a user from your trusted users.
// This is not visible in the Reddit API docs.
func (s *AccountService) RemoveTrusted(ctx context.Context, username string) (*Response, error) {
path := "api/remove_whitelisted"
form := url.Values{}
form.Set("name", username)
// todo: you can also do this with the user id. form.Set("id", id). should we? or is this enough?
req, err := s.client.NewPostForm(path, form)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"testing"
"time"
@ -112,6 +113,15 @@ var expectedRelationships = []Relationship{
},
}
var expectedRelationships2 = []Relationship{
{
ID: "r9_1re60i",
User: "test3",
UserID: "t2_test3",
Created: &Timestamp{time.Date(2020, 3, 6, 2, 27, 0, 0, time.UTC)},
},
}
func TestAccountService_Info(t *testing.T) {
setup()
defer teardown()
@ -230,3 +240,75 @@ func TestAccountService_Blocked(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, expectedRelationships, relationships)
}
func TestAccountService_Messaging(t *testing.T) {
setup()
defer teardown()
blob := readFileContents(t, "testdata/account/messaging.json")
mux.HandleFunc("/prefs/messaging", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodGet, r.Method)
fmt.Fprint(w, blob)
})
blocked, trusted, _, err := client.Account.Messaging(ctx)
assert.NoError(t, err)
assert.Equal(t, expectedRelationships, blocked)
assert.Equal(t, expectedRelationships2, trusted)
}
func TestAccountService_Trusted(t *testing.T) {
setup()
defer teardown()
blob := readFileContents(t, "testdata/account/trusted.json")
mux.HandleFunc("/prefs/trusted", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodGet, r.Method)
fmt.Fprint(w, blob)
})
relationships, _, err := client.Account.Trusted(ctx)
assert.NoError(t, err)
assert.Equal(t, expectedRelationships, relationships)
}
func TestAccountService_AddTrusted(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/api/add_whitelisted", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodPost, r.Method)
form := url.Values{}
form.Set("api_type", "json")
form.Set("name", "test123")
err := r.ParseForm()
assert.NoError(t, err)
assert.Equal(t, form, r.Form)
})
_, err := client.Account.AddTrusted(ctx, "test123")
assert.NoError(t, err)
}
func TestAccountService_RemoveTrusted(t *testing.T) {
setup()
defer teardown()
mux.HandleFunc("/api/remove_whitelisted", func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodPost, r.Method)
form := url.Values{}
form.Set("name", "test123")
err := r.ParseForm()
assert.NoError(t, err)
assert.Equal(t, form, r.Form)
})
_, err := client.Account.RemoveTrusted(ctx, "test123")
assert.NoError(t, err)
}

View File

@ -14,7 +14,7 @@ type RedditError struct {
}
func (e *RedditError) Error() string {
return fmt.Sprintf("%s: %s because of field %q", e.Label, e.Reason, e.Field)
return fmt.Sprintf("field %q caused %s: %s", e.Field, e.Label, e.Reason)
}
// UnmarshalJSON implements the json.Unmarshaler interface.

34
testdata/account/messaging.json vendored Normal file
View File

@ -0,0 +1,34 @@
[
{
"kind": "UserList",
"data": {
"children": [
{
"date": 1593362635,
"rel_id": "r9_1r4879",
"name": "test1",
"id": "t2_test1"
},
{
"date": 1593362642,
"rel_id": "r9_1re930",
"name": "test2",
"id": "t2_test2"
}
]
}
},
{
"kind": "UserList",
"data": {
"children": [
{
"date": 1583461620,
"rel_id": "r9_1re60i",
"name": "test3",
"id": "t2_test3"
}
]
}
}
]

19
testdata/account/trusted.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
"kind": "UserList",
"data": {
"children": [
{
"date": 1593362635,
"rel_id": "r9_1r4879",
"name": "test1",
"id": "t2_test1"
},
{
"date": 1593362642,
"rel_id": "r9_1re930",
"name": "test2",
"id": "t2_test2"
}
]
}
}