diff --git a/app/rest/api/rest.go b/app/rest/api/rest.go index b6c470ab..6f55bd73 100644 --- a/app/rest/api/rest.go +++ b/app/rest/api/rest.go @@ -2,6 +2,8 @@ package api import ( "bytes" + "crypto/sha1" + "encoding/base64" "encoding/json" "fmt" "log" @@ -82,7 +84,7 @@ func (s *Rest) Run(port int) { rapi.Get("/comments", s.findUserCommentsCtrl) rapi.Get("/last/{max}", s.lastCommentsCtrl) rapi.Get("/count", s.countCtrl) - rapi.Post("/count", s.countMultiCtrl) + rapi.Post("/counts", s.countMultiCtrl) rapi.Get("/list", s.listCtrl) rapi.Get("/config", s.configCtrl) rapi.Post("/preview", s.previewCommentCtrl) @@ -379,29 +381,27 @@ func (s *Rest) countCtrl(w http.ResponseWriter, r *http.Request) { // POST /count?site=siteID - get number of comments for posts from post body func (s *Rest) countMultiCtrl(w http.ResponseWriter, r *http.Request) { siteID := r.URL.Query().Get("site") - posts := []string{} - if err := render.DecodeJSON(r.Body, &posts); err != nil { rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get list of posts from request") return } + // key could be long for multiple posts, make it sha1 key := rest.URLKey(r) + strings.Join(posts, ",") - data, err := s.Cache.Get(key, 8*time.Hour, func() ([]byte, error) { - lst, e := s.DataService.List(siteID, 0, 0) + hasher := sha1.New() + if _, err := hasher.Write([]byte(key)); err != nil { + rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't make sha1 for list of urls") + return + } + sha := base64.URLEncoding.EncodeToString(hasher.Sum(nil)) + + data, err := s.Cache.Get(sha, 8*time.Hour, func() ([]byte, error) { + counts, e := s.DataService.Counts(siteID, posts) if e != nil { return nil, e } - res := []store.PostInfo{} - for _, l := range lst { - for _, p := range posts { - if p == l.URL { - res = append(res, l) - } - } - } - return encodeJSONWithHTML(res) + return encodeJSONWithHTML(counts) }) if err != nil { diff --git a/app/rest/api/rest_test.go b/app/rest/api/rest_test.go index 8071dfdc..b55eba5d 100644 --- a/app/rest/api/rest_test.go +++ b/app/rest/api/rest_test.go @@ -329,6 +329,36 @@ func TestServer_Count(t *testing.T) { assert.Equal(t, 2.0, j["count"]) } +func TestServer_Counts(t *testing.T) { + srv, port := prep(t) + assert.NotNil(t, srv) + defer cleanup(srv) + + c1 := store.Comment{Text: "test test #1", + Locator: store.Locator{SiteID: "radio-t", URL: "https://radio-t.com/blah1"}} + c2 := store.Comment{Text: "test test #2", ParentID: "p1", + Locator: store.Locator{SiteID: "radio-t", URL: "https://radio-t.com/blah2"}} + + addComment(t, c1, port) + addComment(t, c1, port) + addComment(t, c1, port) + addComment(t, c2, port) + addComment(t, c2, port) + + r := strings.NewReader(`["https://radio-t.com/blah1","https://radio-t.com/blah2"]`) + resp, err := http.Post(fmt.Sprintf("http://dev:password@127.0.0.1:%d/api/v1/counts?site=radio-t", port), "application/json", r) + assert.Nil(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) + + body, err := ioutil.ReadAll(resp.Body) + assert.Nil(t, err) + + j := []store.PostInfo{} + err = json.Unmarshal(body, &j) + assert.Nil(t, err) + assert.Equal(t, []store.PostInfo([]store.PostInfo{store.PostInfo{URL: "https://radio-t.com/blah1", Count: 3}, store.PostInfo{URL: "https://radio-t.com/blah2", Count: 2}}), j) +} + func TestServer_List(t *testing.T) { srv, port := prep(t) assert.NotNil(t, srv) diff --git a/app/store/service.go b/app/store/service.go index bdefc2d7..0a23a602 100644 --- a/app/store/service.go +++ b/app/store/service.go @@ -79,3 +79,21 @@ func (s *Service) EditComment(locator Locator, commentID string, text string, ed err = s.Put(locator, comment) return comment, err } + +// Counts returns postID+count list for given comments +func (s *Service) Counts(siteID string, commentIDs []string) ([]PostInfo, error) { + list, err := s.List(siteID, 0, 0) + if err != nil { + return nil, err + } + + res := []PostInfo{} + for _, p := range commentIDs { + for _, l := range list { + if p == l.URL { + res = append(res, l) + } + } + } + return res, nil +} diff --git a/app/store/service_test.go b/app/store/service_test.go index dd355ef5..b6111592 100644 --- a/app/store/service_test.go +++ b/app/store/service_test.go @@ -41,7 +41,7 @@ func TestService_Vote(t *testing.T) { assert.Equal(t, map[string]bool{}, res[0].Votes, "vote reset ok") } -func TestBoltDB_Pin(t *testing.T) { +func TestService_Pin(t *testing.T) { defer os.Remove(testDb) b := Service{Interface: prep(t)} @@ -65,7 +65,7 @@ func TestBoltDB_Pin(t *testing.T) { assert.Equal(t, false, c.Pin) } -func TestBoltDB_EditComment(t *testing.T) { +func TestService_EditComment(t *testing.T) { defer os.Remove(testDb) b := Service{Interface: prep(t)} @@ -89,7 +89,7 @@ func TestBoltDB_EditComment(t *testing.T) { assert.NotNil(t, err, "allow edit once") } -func TestBoltDB_EditCommentDurationFailed(t *testing.T) { +func TestService_EditCommentDurationFailed(t *testing.T) { defer os.Remove(testDb) blt, err := NewBoltDB(BoltSite{FileName: "/tmp/test-remark.db", SiteID: "radio-t"}) @@ -118,3 +118,30 @@ func TestBoltDB_EditCommentDurationFailed(t *testing.T) { Edit{Summary: "my edit"}) assert.NotNil(t, err) } + +func TestService_Counts(t *testing.T) { + defer os.Remove(testDb) + b := prep(t) // two comments for https://radio-t.com + + // add one more for https://radio-t.com/2 + comment := Comment{ + Text: `some text, link`, + Timestamp: time.Date(2017, 12, 20, 15, 18, 22, 0, time.Local), + Locator: Locator{URL: "https://radio-t.com/2", SiteID: "radio-t"}, + User: User{ID: "user1", Name: "user name"}, + } + _, err := b.Create(comment) + assert.Nil(t, err) + + svc := Service{Interface: b} + res, err := svc.Counts("radio-t", []string{"https://radio-t.com/2"}) + assert.Nil(t, err) + assert.Equal(t, []PostInfo{PostInfo{URL: "https://radio-t.com/2", Count: 1}}, res) + + res, err = svc.Counts("radio-t", []string{"https://radio-t.com", "https://radio-t.com/2", "blah"}) + assert.Nil(t, err) + assert.Equal(t, []PostInfo{ + PostInfo{URL: "https://radio-t.com", Count: 2}, + PostInfo{URL: "https://radio-t.com/2", Count: 1}, + }, res) +} diff --git a/remark.rest b/remark.rest index 25269420..6065a45d 100644 --- a/remark.rest +++ b/remark.rest @@ -63,13 +63,13 @@ GET {{host}}/api/v1/comments?site=remark&user=disqus_umputun GET {{host}}/api/v1/count?site=remark&url=https://radio-t.com/p/2017/12/16/podcast-576/ ### get counts for many -POST {{host}}/api/v1/count?site=remark +POST {{host}}/api/v1/counts?site=remark Content-Type: application/json [ - "https://radio-t.com/p/2017/12/16/podcast-576/", - "https://radio-t.com/p/2017/12/16/podcast-577/", - "https://radio-t.com/p/2017/12/16/podcast-578/" + "https://radio-t.com/p/2017/12/02/podcast-574/", + "https://radio-t.com/p/2017/12/09/podcast-575/", + "https://radio-t.com/p/2017/12/16/podcast-576/" ] ### list commented posts