simplify store interfaces, siteID instead of locator in many places

This commit is contained in:
Umputun
2018-01-08 19:16:12 -06:00
parent 5c060f3368
commit 1e938702d6
9 changed files with 78 additions and 84 deletions
+2 -2
View File
@@ -19,7 +19,7 @@ type Remark struct {
// Export all comments to writer as json strings. Each comment is one string, separated by "\n"
func (r *Remark) Export(w io.Writer, siteID string) error {
topics, err := r.DataStore.List(store.Locator{SiteID: siteID})
topics, err := r.DataStore.List(siteID)
if err != nil {
return err
}
@@ -27,7 +27,7 @@ func (r *Remark) Export(w io.Writer, siteID string) error {
commentsCount := 0
for _, topic := range topics {
comments, err := r.DataStore.Find(store.Request{Locator: store.Locator{SiteID: siteID, URL: topic}})
comments, err := r.DataStore.Find(store.Locator{SiteID: siteID, URL: topic}, "time")
if err != nil {
return err
}
+1 -1
View File
@@ -45,7 +45,7 @@ func TestRemark_Import(t *testing.T) {
err = r.Import(buf, "radio-t")
assert.Nil(t, err)
comments, err := b.Find(store.Request{Locator: store.Locator{SiteID: "radio-t", URL: "https://radio-t.com"}})
comments, err := b.Find(store.Locator{SiteID: "radio-t", URL: "https://radio-t.com"}, "time")
assert.Nil(t, err)
assert.Equal(t, 2, len(comments))
assert.Equal(t, "efbc17f177ee1a1c0ee6e1e025749966ec071adc", comments[0].ID)
+4 -4
View File
@@ -59,7 +59,7 @@ func (a *admin) setBlockCtrl(w http.ResponseWriter, r *http.Request) {
siteID := r.URL.Query().Get("site")
blockStatus := r.URL.Query().Get("block") == "1"
if err := a.dataService.SetBlock(store.Locator{SiteID: siteID}, userID, blockStatus); err != nil {
if err := a.dataService.SetBlock(siteID, userID, blockStatus); err != nil {
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set blocking status")
return
}
@@ -110,8 +110,8 @@ func (a *admin) importCtrl(w http.ResponseWriter, r *http.Request) {
a.respCache.flush()
}
func (a *admin) checkBlocked(locator store.Locator, user store.User) bool {
return a.dataService.IsBlocked(locator, user.ID)
func (a *admin) checkBlocked(siteID string, user store.User) bool {
return a.dataService.IsBlocked(siteID, user.ID)
}
// processes comments and hides text of all comments for blocked users.
@@ -119,7 +119,7 @@ func (a *admin) checkBlocked(locator store.Locator, user store.User) bool {
func (a *admin) maskBlockedUsers(comments []store.Comment) (res []store.Comment) {
res = make([]store.Comment, len(comments))
for i, c := range comments {
if a.dataService.IsBlocked(c.Locator, c.User.ID) {
if a.dataService.IsBlocked(c.Locator.SiteID, c.User.ID) {
c.User.Blocked = true
c.Text = "this comment was deleted"
c.Score = 0
+9 -10
View File
@@ -139,7 +139,7 @@ func (s *Server) createCommentCtrl(w http.ResponseWriter, r *http.Request) {
log.Printf("[DEBUG] create comment %+v", comment)
// check if user blocked
if s.mod.checkBlocked(store.Locator{}, comment.User) {
if s.mod.checkBlocked(comment.Locator.SiteID, comment.User) {
common.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"), "user blocked")
return
}
@@ -225,7 +225,7 @@ func (s *Server) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
log.Printf("[DEBUG] get comments for %+v", locator)
data, err := s.respCache.get(r.URL.String(), time.Hour, func() ([]byte, error) {
comments, e := s.DataService.Find(store.Request{Locator: locator, Sort: r.URL.Query().Get("sort")})
comments, e := s.DataService.Find(locator, r.URL.Query().Get("sort"))
if e != nil {
return nil, e
}
@@ -258,7 +258,7 @@ func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
}
data, err := s.respCache.get(r.URL.String(), time.Hour, func() ([]byte, error) {
comments, e := s.DataService.Last(store.Locator{SiteID: r.URL.Query().Get("site")}, max)
comments, e := s.DataService.Last(r.URL.Query().Get("site"), max)
if e != nil {
return nil, e
}
@@ -277,11 +277,11 @@ func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
func (s *Server) commentByIDCtrl(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
siteID := r.URL.Query().Get("site")
log.Printf("[DEBUG] get comments by id %s, %+v", id, locator)
log.Printf("[DEBUG] get comments by id %s, %s", id, siteID)
comment, err := s.DataService.GetByID(locator, id)
comment, err := s.DataService.GetByID(siteID, id)
if err != nil {
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't get comment by id")
return
@@ -298,7 +298,7 @@ func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
log.Printf("[DEBUG] get comments by userID %s", userID)
data, err := s.respCache.get(r.URL.String(), time.Hour, func() ([]byte, error) {
comments, e := s.DataService.GetByUser(store.Locator{SiteID: r.URL.Query().Get("site")}, userID)
comments, e := s.DataService.GetByUser(r.URL.Query().Get("site"), userID)
if e != nil {
return nil, e
}
@@ -335,13 +335,12 @@ func (s *Server) countCtrl(w http.ResponseWriter, r *http.Request) {
// GET /list?site=siteID - list posts with comments
func (s *Server) listCtrl(w http.ResponseWriter, r *http.Request) {
locator := store.Locator{SiteID: r.URL.Query().Get("site")}
posts, err := s.DataService.List(locator)
posts, err := s.DataService.List(r.URL.Query().Get("site"))
if err != nil {
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get count")
return
}
render.JSON(w, r, JSON{"posts": posts, "loc": locator})
render.JSON(w, r, JSON{"posts": posts, "site": r.URL.Query().Get("site")})
}
// PUT /vote/{id}?site=siteID&url=post-url&vote=1 - vote for/against comment
+23 -23
View File
@@ -151,18 +151,18 @@ func (b *BoltDB) Delete(locator Locator, commentID string) error {
}
// Find returns all comments for post and sorts results
func (b *BoltDB) Find(request Request) (comments []Comment, err error) {
func (b *BoltDB) Find(locator Locator, sortFld string) (comments []Comment, err error) {
comments = []Comment{}
bdb, err := b.db(request.Locator.SiteID)
bdb, err := b.db(locator.SiteID)
if err != nil {
return nil, err
}
err = bdb.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(request.Locator.URL))
bucket := tx.Bucket([]byte(locator.URL))
if bucket == nil {
return errors.Errorf("no bucket %s in store", request.Locator.URL)
return errors.Errorf("no bucket %s in store", locator.URL)
}
return bucket.ForEach(func(k, v []byte) error {
@@ -175,17 +175,17 @@ func (b *BoltDB) Find(request Request) (comments []Comment, err error) {
})
})
// sort result according to request.Sort
// sort result according to sortFld
sort.Slice(comments, func(i, j int) bool {
switch request.Sort {
switch sortFld {
case "+time", "-time", "time":
if strings.HasPrefix(request.Sort, "-") {
if strings.HasPrefix(sortFld, "-") {
return comments[i].Timestamp.After(comments[j].Timestamp)
}
return comments[i].Timestamp.Before(comments[j].Timestamp)
case "+score", "-score", "score":
if strings.HasPrefix(request.Sort, "-") {
if strings.HasPrefix(sortFld, "-") {
return comments[i].Score > comments[j].Score
}
return comments[i].Score < comments[j].Score
@@ -199,9 +199,9 @@ func (b *BoltDB) Find(request Request) (comments []Comment, err error) {
}
// GetByID returns comment by id across posts
func (b *BoltDB) GetByID(locator Locator, commentID string) (comment Comment, err error) {
func (b *BoltDB) GetByID(siteID string, commentID string) (comment Comment, err error) {
bdb, err := b.db(locator.SiteID)
bdb, err := b.db(siteID)
if err != nil {
return comment, err
}
@@ -220,7 +220,7 @@ func (b *BoltDB) GetByID(locator Locator, commentID string) (comment Comment, er
return e
}
if foundID == commentID && url == locator.URL {
if foundID == commentID {
urlBucket := tx.Bucket([]byte(url))
if urlBucket == nil {
return errors.Errorf("no bucket %s in store", url)
@@ -235,14 +235,14 @@ func (b *BoltDB) GetByID(locator Locator, commentID string) (comment Comment, er
return comment, err
}
// Last returns up to max last comments for given locator
func (b *BoltDB) Last(locator Locator, max int) (comments []Comment, err error) {
// Last returns up to max last comments for given siteID
func (b *BoltDB) Last(siteID string, max int) (comments []Comment, err error) {
if max > lastLimit || max == 0 {
max = lastLimit
}
bdb, err := b.db(locator.SiteID)
bdb, err := b.db(siteID)
if err != nil {
return nil, err
}
@@ -302,9 +302,9 @@ func (b *BoltDB) Count(locator Locator) (count int, err error) {
}
// SetBlock blocks/unblocks user for given site
func (b *BoltDB) SetBlock(locator Locator, userID string, status bool) error {
func (b *BoltDB) SetBlock(siteID string, userID string, status bool) error {
bdb, err := b.db(locator.SiteID)
bdb, err := b.db(siteID)
if err != nil {
return err
}
@@ -331,9 +331,9 @@ func (b *BoltDB) SetBlock(locator Locator, userID string, status bool) error {
}
// IsBlocked checks if user blocked
func (b *BoltDB) IsBlocked(locator Locator, userID string) (blocked bool) {
func (b *BoltDB) IsBlocked(siteID string, userID string) (blocked bool) {
bdb, err := b.db(locator.SiteID)
bdb, err := b.db(siteID)
if err != nil {
return false
}
@@ -350,9 +350,9 @@ func (b *BoltDB) IsBlocked(locator Locator, userID string) (blocked bool) {
}
// List returns list of buckets, which is list of all commented posts
func (b BoltDB) List(locator Locator) (list []string, err error) {
func (b BoltDB) List(siteID string) (list []string, err error) {
bdb, err := b.db(locator.SiteID)
bdb, err := b.db(siteID)
if err != nil {
return nil, err
}
@@ -370,12 +370,12 @@ func (b BoltDB) List(locator Locator) (list []string, err error) {
// GetByUser 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) GetByUser(locator Locator, userID string) (comments []Comment, err error) {
func (b *BoltDB) GetByUser(siteID string, userID string) (comments []Comment, err error) {
comments = []Comment{}
commentRefs := []string{}
bdb, err := b.db(locator.SiteID)
bdb, err := b.db(siteID)
if err != nil {
return nil, err
}
@@ -411,7 +411,7 @@ func (b *BoltDB) GetByUser(locator Locator, userID string) (comments []Comment,
if e != nil {
return comments, errors.Wrapf(e, "can't parse reference %s", v)
}
if c, e := b.GetByID(Locator{URL: url, SiteID: locator.SiteID}, commentID); e == nil {
if c, e := b.Get(Locator{SiteID: siteID, URL: url}, commentID); e == nil {
comments = append(comments, c)
}
}
+17 -17
View File
@@ -14,7 +14,7 @@ func TestBoltDB_CreateAndFind(t *testing.T) {
var b Interface = prep(t)
defer os.Remove(testDb)
res, err := b.Find(Request{Locator: Locator{URL: "https://radio-t.com", SiteID: "radio-t"}})
res, err := b.Find(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "time")
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
assert.Equal(t, `some text, <a href="http://radio-t.com" rel="nofollow">link</a>`, res[0].Text)
@@ -27,19 +27,19 @@ func TestBoltDB_Delete(t *testing.T) {
b := prep(t)
loc := Locator{URL: "https://radio-t.com", SiteID: "radio-t"}
res, err := b.Find(Request{Locator: loc})
res, err := b.Find(loc, "time")
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
err = b.Delete(loc, res[0].ID)
assert.Nil(t, err)
res, err = b.Find(Request{Locator: loc})
res, err = b.Find(loc, "time")
assert.Nil(t, err)
assert.Equal(t, 1, len(res))
assert.Equal(t, "some text2", res[0].Text)
comments, err := b.Last(loc, 10)
comments, err := b.Last("radio-t", 10)
assert.Nil(t, err)
assert.Equal(t, 1, len(comments), "only 1 left in last")
}
@@ -48,15 +48,15 @@ func TestBoltDB_GetByID(t *testing.T) {
defer os.Remove(testDb)
b := prep(t)
res, err := b.Find(Request{Locator: Locator{URL: "https://radio-t.com", SiteID: "radio-t"}})
res, err := b.Find(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "time")
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
comment, err := b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[1].ID)
comment, err := b.GetByID("radio-t", res[1].ID)
assert.Nil(t, err)
assert.Equal(t, "some text2", comment.Text)
comment, err = b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "1234567")
comment, err = b.GetByID("radio-t", "1234567")
assert.NotNil(t, err)
}
@@ -64,12 +64,12 @@ func TestBoltDB_Last(t *testing.T) {
defer os.Remove(testDb)
b := prep(t)
res, err := b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
res, err := b.Last("radio-t", 0)
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
assert.Equal(t, "some text2", res[0].Text)
res, err = b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 1)
res, err = b.Last("radio-t", 1)
assert.Nil(t, err)
assert.Equal(t, 1, len(res))
assert.Equal(t, "some text2", res[0].Text)
@@ -88,15 +88,15 @@ func TestBoltDB_BlockUser(t *testing.T) {
defer os.Remove(testDb)
b := prep(t)
assert.False(t, b.IsBlocked(Locator{SiteID: "radio-t"}, "user1"), "nothing blocked")
assert.False(t, b.IsBlocked("radio-t", "user1"), "nothing blocked")
assert.NoError(t, b.SetBlock(Locator{SiteID: "radio-t"}, "user1", true))
assert.True(t, b.IsBlocked(Locator{SiteID: "radio-t"}, "user1"), "user1 blocked")
assert.NoError(t, b.SetBlock("radio-t", "user1", true))
assert.True(t, b.IsBlocked("radio-t", "user1"), "user1 blocked")
assert.False(t, b.IsBlocked(Locator{SiteID: "radio-t"}, "user2"), "user2 still unblocked")
assert.False(t, b.IsBlocked("radio-t", "user2"), "user2 still unblocked")
assert.NoError(t, b.SetBlock(Locator{SiteID: "radio-t"}, "user1", false))
assert.False(t, b.IsBlocked(Locator{SiteID: "radio-t"}, "user1"), "user1 unblocked")
assert.NoError(t, b.SetBlock("radio-t", "user1", false))
assert.False(t, b.IsBlocked("radio-t", "user1"), "user1 unblocked")
}
@@ -114,7 +114,7 @@ func TestBoltDB_List(t *testing.T) {
_, err := b.Create(comment)
assert.Nil(t, err)
res, err := b.List(Locator{SiteID: "radio-t"})
res, err := b.List("radio-t")
assert.Nil(t, err)
assert.Equal(t, []string{"https://radio-t.com", "https://radio-t.com/2"}, res)
}
@@ -123,7 +123,7 @@ func TestBoltDB_GetForUser(t *testing.T) {
defer os.Remove(testDb)
b := prep(t)
res, err := b.GetByUser(Locator{SiteID: "radio-t"}, "user1")
res, err := b.GetByUser("radio-t", "user1")
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
assert.Equal(t, "some text2", res[0].Text, "sorted by -time")
+9 -9
View File
@@ -12,7 +12,7 @@ func TestService_Vote(t *testing.T) {
defer os.Remove(testDb)
b := Service{Interface: prep(t)}
res, err := b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
res, err := b.Last("radio-t", 0)
t.Logf("%+v", res[0])
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
@@ -27,14 +27,14 @@ func TestService_Vote(t *testing.T) {
_, err = b.Vote(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", true)
assert.NotNil(t, err, "double-voting rejected")
res, err = b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
res, err = b.Last("radio-t", 0)
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
assert.Equal(t, 1, res[0].Score)
_, err = b.Vote(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", false)
assert.Nil(t, err, "vote reset")
res, err = b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
res, err = b.Last("radio-t", 0)
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
assert.Equal(t, 0, res[0].Score)
@@ -46,7 +46,7 @@ func TestBoltDB_Pin(t *testing.T) {
defer os.Remove(testDb)
b := Service{Interface: prep(t)}
res, err := b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
res, err := b.Last("radio-t", 0)
t.Logf("%+v", res[0])
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
@@ -55,13 +55,13 @@ func TestBoltDB_Pin(t *testing.T) {
err = b.SetPin(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, true)
assert.Nil(t, err)
c, err := b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID)
c, err := b.GetByID("radio-t", res[0].ID)
assert.Nil(t, err)
assert.Equal(t, true, c.Pin)
err = b.SetPin(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, false)
assert.Nil(t, err)
c, err = b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID)
c, err = b.GetByID("radio-t", res[0].ID)
assert.Nil(t, err)
assert.Equal(t, false, c.Pin)
}
@@ -70,7 +70,7 @@ func TestBoltDB_EditComment(t *testing.T) {
defer os.Remove(testDb)
b := Service{Interface: prep(t)}
res, err := b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
res, err := b.Last("radio-t", 0)
t.Logf("%+v", res[0])
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
@@ -81,7 +81,7 @@ func TestBoltDB_EditComment(t *testing.T) {
assert.Equal(t, "my edit", comment.Edit.Summary)
assert.Equal(t, "xxx", comment.Text)
c, err := b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID)
c, err := b.GetByID("radio-t", res[0].ID)
assert.Nil(t, err)
assert.Equal(t, "my edit", c.Edit.Summary)
assert.Equal(t, "xxx", c.Text)
@@ -104,7 +104,7 @@ func TestBoltDB_EditCommentDurationFailed(t *testing.T) {
b := Service{Interface: blt, EditDuration: 100 * time.Millisecond}
res, err := b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
res, err := b.Last("radio-t", 0)
t.Logf("%+v", res[0])
assert.Nil(t, err)
assert.Equal(t, 1, len(res))
+8 -16
View File
@@ -51,14 +51,6 @@ type Edit struct {
Summary string `json:"summary"`
}
// Request is a container for all finds
type Request struct {
Locator Locator `json:"locator"`
Sort string `json:"sort"`
Offset int `json:"offset"`
Limit int `json:"limit"`
}
// Interface combines all store interfaces
type Interface interface {
Accessor
@@ -70,19 +62,19 @@ type Accessor interface {
Create(comment Comment) (commentID string, err error) // create new comment, avoid dups by ID
Get(locator Locator, commentID string) (comment Comment, err error) // get comment by ID
Put(locator Locator, comment Comment) error // update comment, mutable parts only
Find(request Request) ([]Comment, error) // find comments for request
Last(locator Locator, max int) ([]Comment, error) // last comments for given site
GetByID(locator Locator, commentID string) (Comment, error) // comment by id
GetByUser(locator Locator, userID string) ([]Comment, error) // comment by user
Find(locator Locator, sort string) ([]Comment, error) // find comments for request
Last(siteID string, max int) ([]Comment, error) // last comments for given site
GetByID(siteID string, commentID string) (Comment, error) // comment by id
GetByUser(siteID string, userID string) ([]Comment, error) // comment by user
Count(locator Locator) (int, error) // number of comments for the post
List(locator Locator) ([]string, error) // list of commented posts
List(siteID string) ([]string, error) // list of commented posts
}
// Admin defines all store ops avail for admin only
type Admin interface {
Delete(locator Locator, commentID string) error // delete comment by id
SetBlock(locator Locator, userID string, status bool) error // block or unblock user
IsBlocked(locator Locator, userID string) bool // check if user blocked
Delete(locator Locator, commentID string) error // delete comment by id
SetBlock(siteID string, userID string, status bool) error // block or unblock user
IsBlocked(siteID string, userID string) bool // check if user blocked
}
// makeCommentID generates sha1(random) string
+5 -2
View File
@@ -1,7 +1,10 @@
package store
import "testing"
import "github.com/stretchr/testify/assert"
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestStore_MakeCommentID(t *testing.T) {
cid1 := makeCommentID()