find user comments to support pagination

This commit is contained in:
Umputun
2018-06-01 22:17:57 -05:00
parent e4176e2afd
commit 837b2ef719
8 changed files with 93 additions and 20 deletions
-1
View File
@@ -5,7 +5,6 @@
/web/public/
/.vscode/
/.idea/
#/.git/
# source files
docker-compose.yml
+4 -1
View File
@@ -31,7 +31,10 @@ RUN if [ -z "$COVERALLS_TOKEN" ] ; then \
echo coverall not enabled ; \
else goveralls -coverprofile=.cover/cover.out -service=travis-ci -repotoken $COVERALLS_TOKEN; fi
RUN go build -o remark -ldflags "-X main.revision=$(git rev-parse --abbrev-ref HEAD)-$(git describe --abbrev=7 --always --tags)-$(date +%Y%m%d-%H:%M:%S) -s -w" ./app
RUN \
version=$(git rev-parse --abbrev-ref HEAD)-$(git describe --abbrev=7 --always --tags)-$(date +%Y%m%d-%H:%M:%S) && \
echo "version $version" && \
go build -o remark -ldflags "-X main.revision=${version} -s -w" ./app
FROM node:9.4-alpine as build-frontend
+1 -1
View File
@@ -444,7 +444,7 @@ func (s *Rest) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
log.Printf("[DEBUG] get comments for userID %s, %s", userID, siteID)
data, err := s.Cache.Get(cache.Key(cache.URLKey(r), userID, siteID), func() ([]byte, error) {
comments, count, e := s.DataService.User(siteID, userID, limit)
comments, count, e := s.DataService.User(siteID, userID, limit, 0)
if e != nil {
return nil, e
}
+6 -1
View File
@@ -294,7 +294,7 @@ func (b *BoltDB) Info(locator store.Locator, readOnlyAge int) (store.PostInfo, e
// User extracts all comments for given site and given userID
// "users" bucket has sub-bucket for each userID, and keeps it as ts:ref
func (b *BoltDB) User(siteID string, userID string, limit int) (comments []store.Comment, totalComments int, err error) {
func (b *BoltDB) User(siteID, userID string, limit, skip int) (comments []store.Comment, totalComments int, err error) {
comments = []store.Comment{}
commentRefs := []string{}
@@ -318,8 +318,13 @@ func (b *BoltDB) User(siteID string, userID string, limit int) (comments []store
c := userIDBkt.Cursor()
totalComments = 0
skipComments := 0
for k, v := c.Last(); k != nil; k, v = c.Prev() {
totalComments++
if skip > 0 && skipComments < skip {
skipComments++
continue
}
if len(commentRefs) < limit {
commentRefs = append(commentRefs, string(v))
}
+62 -4
View File
@@ -1,6 +1,7 @@
package engine
import (
"fmt"
"os"
"testing"
"time"
@@ -224,25 +225,82 @@ func TestBoltDB_GetForUser(t *testing.T) {
defer os.Remove(testDb)
b := prep(t)
res, count, err := b.User("radio-t", "user1", 5)
res, count, err := b.User("radio-t", "user1", 5, 0)
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
assert.Equal(t, 2, count)
assert.Equal(t, "some text2", res[0].Text, "sorted by -time")
res, count, err = b.User("radio-t", "user1", 1)
res, count, err = b.User("radio-t", "user1", 1, 0)
assert.Nil(t, err)
assert.Equal(t, 1, len(res), "allow 1 comment")
assert.Equal(t, 2, count)
assert.Equal(t, "some text2", res[0].Text, "sorted by -time")
_, _, err = b.User("bad", "user1", 1)
res, count, err = b.User("radio-t", "user1", 1, 1)
assert.Nil(t, err)
assert.Equal(t, 1, len(res), "allow 1 comment")
assert.Equal(t, 2, count)
assert.Equal(t, `some text, <a href="http://radio-t.com">link</a>`, res[0].Text, "second comment")
_, _, err = b.User("bad", "user1", 1, 0)
assert.EqualError(t, err, `site "bad" not found`)
_, _, err = b.User("radio-t", "userZ", 1)
_, _, err = b.User("radio-t", "userZ", 1, 0)
assert.EqualError(t, err, `no comments for user userZ in store`)
}
func TestBoltDB_GetForUserPagination(t *testing.T) {
os.Remove(testDb)
b, err := NewBoltDB(bolt.Options{}, BoltSite{FileName: testDb, SiteID: "radio-t"})
require.Nil(t, err)
defer os.Remove(testDb)
c := store.Comment{
Locator: store.Locator{URL: "https://radio-t.com", SiteID: "radio-t"},
User: store.User{ID: "user1", Name: "user name"},
}
// write 50 comments
for i := 0; i < 50; i++ {
c.ID = fmt.Sprintf("id-%d", i)
c.Text = fmt.Sprintf("text #%d", i)
c.Timestamp = time.Date(2017, 12, 20, 15, 18, i, 0, time.Local)
_, err = b.Create(c)
require.Nil(t, err)
}
// seek 0, 5 comments
res, count, err := b.User("radio-t", "user1", 5, 0)
assert.Nil(t, err)
assert.Equal(t, 50, count)
assert.Equal(t, 5, len(res))
assert.Equal(t, "id-49", res[0].ID)
assert.Equal(t, "id-45", res[4].ID)
// seek 10, 3 comments
res, count, err = b.User("radio-t", "user1", 3, 10)
assert.Nil(t, err)
assert.Equal(t, 50, count)
assert.Equal(t, 3, len(res))
assert.Equal(t, "id-39", res[0].ID)
assert.Equal(t, "id-37", res[2].ID)
// seek 45, ask 10 comments
res, count, err = b.User("radio-t", "user1", 10, 45)
assert.Nil(t, err)
assert.Equal(t, 50, count)
assert.Equal(t, 5, len(res))
assert.Equal(t, "id-4", res[0].ID)
assert.Equal(t, "id-0", res[4].ID)
// seek 55, ask 10 comments
res, count, err = b.User("radio-t", "user1", 10, 55)
assert.Nil(t, err)
assert.Equal(t, 50, count)
assert.Equal(t, 0, len(res))
}
func TestBoltDB_Ref(t *testing.T) {
b := BoltDB{}
comment := store.Comment{
+2 -2
View File
@@ -206,10 +206,10 @@ func (b *BoltDB) Blocked(siteID string) (users []store.BlockedUser, err error) {
// get user name from comment user section
userName := ""
if userComments, _, e := b.User(siteID, string(k), 1); e == nil && len(userComments) > 0 {
userComments, _, e := b.User(siteID, string(k), 1, 0)
if e == nil && len(userComments) > 0 {
userName = userComments[0].User.Name
}
users = append(users, store.BlockedUser{ID: string(k), Name: userName, Timestamp: ts})
return nil
})
+1 -1
View File
@@ -109,7 +109,7 @@ func TestBoltAdmin_DeleteUser(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, 0, c, "0 count")
_, _, err = b.User("radio-t", "user1", 5)
_, _, err = b.User("radio-t", "user1", 5, 0)
assert.EqualError(t, err, "no comments for user user1 in store")
comments, err := b.Last("radio-t", 10)
+17 -9
View File
@@ -17,17 +17,25 @@ type Interface interface {
Admin
}
// UserRequest is the request send to get comments by user
type UserRequest struct {
SiteID string
UserID string
Limit int
Skip int
}
// Accessor defines all usual access ops avail for regular user
type Accessor interface {
Create(comment store.Comment) (commentID string, err error) // create new comment, avoid dups by id
Get(locator store.Locator, commentID string) (store.Comment, error) // get comment by id
Put(locator store.Locator, comment store.Comment) error // update comment, mutable parts only
Find(locator store.Locator, sort string) ([]store.Comment, error) // find comments for locator
Last(siteID string, limit int) ([]store.Comment, error) // last comments for given site, sorted by time
User(siteID string, userID string, limit int) ([]store.Comment, int, error) // comments by user, sorted by time
Count(locator store.Locator) (int, error) // number of comments for the post
List(siteID string, limit int, skip int) ([]store.PostInfo, error) // list of commented posts
Info(locator store.Locator, readonlyAge int) (store.PostInfo, error) // get post info
Create(comment store.Comment) (commentID string, err error) // create new comment, avoid dups by id
Get(locator store.Locator, commentID string) (store.Comment, error) // get comment by id
Put(locator store.Locator, comment store.Comment) error // update comment, mutable parts only
Find(locator store.Locator, sort string) ([]store.Comment, error) // find comments for locator
Last(siteID string, limit int) ([]store.Comment, error) // last comments for given site, sorted by time
User(siteID, userID string, limit, skip int) ([]store.Comment, int, error) // comments by user, sorted by time
Count(locator store.Locator) (int, error) // number of comments for the post
List(siteID string, limit int, skip int) ([]store.PostInfo, error) // list of commented posts
Info(locator store.Locator, readonlyAge int) (store.PostInfo, error) // get post info
}
// Admin defines all store ops avail for admin only