From 3b5a1a62de069a480a62ce0a7fa153c0bbebd7ee Mon Sep 17 00:00:00 2001 From: Umputun Date: Mon, 25 Mar 2019 16:14:10 -0500 Subject: [PATCH] add vote for the current user, hide list of other votes #297 --- backend/app/rest/api/rest.go | 35 +++++++++++++++++++++++ backend/app/rest/api/rest_private_test.go | 21 ++++++++++---- backend/app/rest/api/rest_public.go | 8 +++--- backend/app/rest/api/rss.go | 6 ++-- backend/app/store/comment.go | 3 +- 5 files changed, 60 insertions(+), 13 deletions(-) diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index ccad9e37..ad788e5e 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -281,6 +281,41 @@ func (s *Rest) routes() chi.Router { return router } +func (s *Rest) alterComments(comments []store.Comment, r *http.Request) (res []store.Comment) { + + res = s.adminService.alterComments(comments, r) // apply admin's alteration + + // prepare vote info for client view + vote := func(c store.Comment, r *http.Request) store.Comment { + + c.Vote = 0 //default is "none" (not voted) + + user, err := rest.GetUserInfo(r) + if err != nil { + c.Votes = nil // hide voters list }() + return c + } + + if v, ok := c.Votes[user.ID]; ok { + if v { + c.Vote = 1 + } else { + c.Vote = -1 + } + } + + c.Votes = nil // hide voters list }() + return c + } + + for i, c := range res { + c = vote(c, r) + res[i] = c + } + + return res +} + // serves static files from /web or embedded by statik func addFileServer(r chi.Router, path string, root http.FileSystem) { diff --git a/backend/app/rest/api/rest_private_test.go b/backend/app/rest/api/rest_private_test.go index 6131bfb0..5f7cb1d2 100644 --- a/backend/app/rest/api/rest_private_test.go +++ b/backend/app/rest/api/rest_private_test.go @@ -343,7 +343,8 @@ func TestRest_Vote(t *testing.T) { req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/v1/vote/%s?site=radio-t&url=https://radio-t.com/blah&vote=%d", ts.URL, id1, val), nil) assert.Nil(t, err) - req.SetBasicAuth("admin", "password") + req.Header.Add("X-JWT", devToken) + //req.SetBasicAuth("admin", "password") resp, err := client.Do(req) assert.Nil(t, err) return resp.StatusCode @@ -351,22 +352,32 @@ func TestRest_Vote(t *testing.T) { assert.Equal(t, 200, vote(1), "first vote allowed") assert.Equal(t, 400, vote(1), "second vote rejected") - body, code := get(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) + body, code := getWithDevAuth(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) assert.Equal(t, 200, code) cr := store.Comment{} err := json.Unmarshal([]byte(body), &cr) assert.Nil(t, err) assert.Equal(t, 1, cr.Score) - assert.Equal(t, map[string]bool{"admin": true}, cr.Votes) + assert.Equal(t, 1, cr.Vote) + assert.Equal(t, map[string]bool(nil), cr.Votes) - assert.Equal(t, 200, vote(-1), "opposite vote allowed") body, code = get(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) assert.Equal(t, 200, code) cr = store.Comment{} err = json.Unmarshal([]byte(body), &cr) assert.Nil(t, err) + assert.Equal(t, 1, cr.Score) + assert.Equal(t, 0, cr.Vote, "no vote info for not authed user") + assert.Equal(t, map[string]bool(nil), cr.Votes) + + assert.Equal(t, 200, vote(-1), "opposite vote allowed") + body, code = getWithDevAuth(t, fmt.Sprintf("%s/api/v1/id/%s?site=radio-t&url=https://radio-t.com/blah", ts.URL, id1)) + assert.Equal(t, 200, code) + cr = store.Comment{} + err = json.Unmarshal([]byte(body), &cr) + assert.Nil(t, err) assert.Equal(t, 0, cr.Score) - assert.Equal(t, map[string]bool{}, cr.Votes) + assert.Equal(t, map[string]bool(nil), cr.Votes) } func TestRest_UserAllData(t *testing.T) { diff --git a/backend/app/rest/api/rest_public.go b/backend/app/rest/api/rest_public.go index 40c8e99e..4d2c87d4 100644 --- a/backend/app/rest/api/rest_public.go +++ b/backend/app/rest/api/rest_public.go @@ -33,7 +33,7 @@ func (s *Rest) findCommentsCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { comments = []store.Comment{} // error should clear comments and continue for post info } - maskedComments := s.adminService.alterComments(comments, r) + maskedComments := s.alterComments(comments, r) var b []byte switch r.URL.Query().Get("format") { case "tree": @@ -129,7 +129,7 @@ func (s *Rest) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { return nil, e } - comments = s.adminService.alterComments(comments, r) + comments = s.alterComments(comments, r) // filter deleted from last comments view. Blocked marked as deleted and will sneak in without filterDeleted := filterComments(comments, func(c store.Comment) bool { return !c.Deleted }) return encodeJSONWithHTML(filterDeleted) @@ -159,7 +159,7 @@ func (s *Rest) commentByIDCtrl(w http.ResponseWriter, r *http.Request) { rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get comment by id", rest.ErrCommentNotFound) return } - comment = s.adminService.alterComments([]store.Comment{comment}, r)[0] + comment = s.alterComments([]store.Comment{comment}, r)[0] render.Status(r, http.StatusOK) if err = R.RenderJSONWithHTML(w, r, comment); err != nil { @@ -191,7 +191,7 @@ func (s *Rest) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { return nil, e } - comments = s.adminService.alterComments(comments, r) + comments = s.alterComments(comments, r) comments = filterComments(comments, func(c store.Comment) bool { return !c.Deleted }) count, e := s.DataService.UserCount(siteID, userID) if e != nil { diff --git a/backend/app/rest/api/rss.go b/backend/app/rest/api/rss.go index 1573befc..fc1c473d 100644 --- a/backend/app/rest/api/rss.go +++ b/backend/app/rest/api/rss.go @@ -41,7 +41,7 @@ func (s *Rest) rssPostCommentsCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { return nil, e } - comments = s.adminService.alterComments(comments, r) + comments = s.alterComments(comments, r) rss, e := s.toRssFeed(locator.URL, comments, "post comments for "+r.URL.Query().Get("url")) if e != nil { return nil, e @@ -73,7 +73,7 @@ func (s *Rest) rssSiteCommentsCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { return nil, e } - comments = s.adminService.alterComments(comments, r) + comments = s.alterComments(comments, r) rss, e := s.toRssFeed(r.URL.Query().Get("site"), comments, "site comment for "+siteID) if e != nil { @@ -107,7 +107,7 @@ func (s *Rest) rssRepliesCtrl(w http.ResponseWriter, r *http.Request) { if e != nil { return nil, errors.Wrap(e, "can't get last comments") } - comments = s.adminService.alterComments(comments, r) + comments = s.alterComments(comments, r) replies := []store.Comment{} for _, c := range comments { if len(replies) > maxRssItems || c.Timestamp.Add(maxReplyDuration).Before(time.Now()) { diff --git a/backend/app/store/comment.go b/backend/app/store/comment.go index 895c3962..6bc43490 100644 --- a/backend/app/store/comment.go +++ b/backend/app/store/comment.go @@ -17,7 +17,8 @@ type Comment struct { User User `json:"user"` Locator Locator `json:"locator"` Score int `json:"score"` - Votes map[string]bool `json:"votes"` + Votes map[string]bool `json:"votes,omitempty"` + Vote int `json:"vote"` // vote for the current user Controversy float64 `json:"controversy,omitempty"` Timestamp time.Time `json:"time" bson:"time"` Edit *Edit `json:"edit,omitempty" bson:"edit,omitempty"` // pointer to have empty default in json response