integrate multi-site bolt store

This commit is contained in:
Umputun
2017-12-27 17:54:58 -06:00
parent c3f8fc2e49
commit f907533da8
10 changed files with 181 additions and 96 deletions
+12 -7
View File
@@ -16,9 +16,10 @@ import (
)
var opts struct {
DBFile string `long:"db" env:"BOLTDB_FILE" default:"/tmp/remark.db" description:"bolt file name"`
SiteURL string `long:"site-url" env:"REMARK_URL" default:"http://remark.umputun.com:8080" description:"url to remark"`
Admins []string `long:"admin" env:"ADMIN" default:"umputun@gmail.com" description:"admin(s) names" env-delim:","`
BoltPath string `long:"bolt" env:"BOLTDB_PATH" default:"/tmp" description:"parent dir for bolt files"`
Sites []string `long:"site" env:"SITE" default:"demo" description:"site names" env-delim:","`
RemarkURL string `long:"url" env:"REMARK_URL" default:"http://remark.umputun.com:8080" description:"url to remark"`
Admins []string `long:"admin" env:"ADMIN" default:"umputun@gmail.com" description:"admin(s) names" env-delim:","`
DevMode bool `long:"dev" env:"DEV" description:"development mode, no auth enforced"`
Dbg bool `long:"dbg" env:"DEBUG" description:"debug mode"`
@@ -37,7 +38,7 @@ var opts struct {
ImportCommand struct {
Provider string `long:"provider" default:"disqus" description:"provider type"`
SiteID string `long:"site" default:"site" description:"site ID"`
SiteID string `long:"site" default:"demo" description:"site ID"`
InputFile string `long:"file" default:"disqus.xml" description:"input file"`
} `command:"import" description:"import comments from external sources"`
}
@@ -54,7 +55,11 @@ func main() {
setupLog(opts.Dbg)
log.Print("[INFO] started remark")
dataStore, err := store.NewBoltDB(opts.DBFile)
boltSites := []store.BoltSite{}
for _, site := range opts.Sites {
boltSites = append(boltSites, store.BoltSite{SiteID: site, FileName: fmt.Sprintf("%s/%s.db", opts.BoltPath, site)})
}
dataStore, err := store.NewBoltDB(boltSites...)
if err != nil {
log.Fatalf("[ERROR] can't initialize data store, %+v", err)
}
@@ -87,13 +92,13 @@ func main() {
Cid: opts.ServerCommand.GoogleCID,
Csecret: opts.ServerCommand.GoogleCSEC,
SessionStore: sessionStore,
SiteURL: opts.SiteURL,
RemarkURL: opts.RemarkURL,
}),
AuthGithub: auth.NewGithub(auth.Params{
Cid: opts.ServerCommand.GithubCID,
Csecret: opts.ServerCommand.GithubCSEC,
SessionStore: sessionStore,
SiteURL: opts.SiteURL,
RemarkURL: opts.RemarkURL,
}),
}
+1 -1
View File
@@ -33,7 +33,7 @@ func TestRemark_Export(t *testing.T) {
func prep(t *testing.T) *store.BoltDB {
os.Remove(testDb)
b, err := store.NewBoltDB(testDb)
b, err := store.NewBoltDB(store.BoltSite{SiteID: "radio-t", FileName: testDb})
assert.Nil(t, err)
comment := store.Comment{
+9 -9
View File
@@ -30,14 +30,14 @@ func (a *admin) routes() chi.Router {
return router
}
// DELETE /comment/{id}?url=post-url
// DELETE /comment/{id}?site=siteID&url=post-url
func (a *admin) deleteCommentCtrl(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")}
log.Printf("[INFO] delete comment %s", id)
url := r.URL.Query().Get("url")
err := a.dataService.Delete(store.Locator{URL: url}, id)
err := a.dataService.Delete(locator, id)
if err != nil {
log.Printf("[WARN] can't delete comment, %s", err)
httpError(w, r, http.StatusInternalServerError, err, "can't delete comment")
@@ -45,7 +45,7 @@ func (a *admin) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
}
a.respCache.Flush()
render.Status(r, http.StatusOK)
render.JSON(w, r, JSON{"id": id, "url": url})
render.JSON(w, r, JSON{"id": id, "loc": locator})
}
// PUT /user/{userid}?site=side-id&block=1
@@ -62,18 +62,18 @@ func (a *admin) setBlockCtrl(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, JSON{"user_id": userID, "site_id": siteID, "block": blockStatus})
}
// PUT /pin/{id}?url=post-url&pin=1
// PUT /pin/{id}?site=siteID&url=post-url&pin=1
func (a *admin) setPinCtrl(w http.ResponseWriter, r *http.Request) {
commentID := chi.URLParam(r, "id")
url := r.URL.Query().Get("url")
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
pinStatus := r.URL.Query().Get("pin") == "1"
if err := a.dataService.SetPin(store.Locator{URL: url}, commentID, pinStatus); err != nil {
if err := a.dataService.SetPin(locator, commentID, pinStatus); err != nil {
httpError(w, r, http.StatusBadRequest, err, "can't set pin status")
return
}
a.respCache.Flush()
render.JSON(w, r, JSON{"id": commentID, "url": url, "pin": pinStatus})
render.JSON(w, r, JSON{"id": commentID, "loc": locator, "pin": pinStatus})
}
// GET /export?site=site-id
@@ -84,5 +84,5 @@ func (a *admin) exportCtrl(w http.ResponseWriter, r *http.Request) {
}
}
func (a *admin) checkBlocked(locator store.Locator, user store.User) bool {
return a.dataService.IsBlocked(store.Locator{}, user.ID)
return a.dataService.IsBlocked(locator, user.ID)
}
+1 -1
View File
@@ -37,7 +37,7 @@ type Params struct {
Cid string
Csecret string
SessionStore *sessions.FilesystemStore
SiteURL string
RemarkURL string
}
// newProvider makes auth for given provider
+2 -2
View File
@@ -14,7 +14,7 @@ func NewGoogle(p Params) *Provider {
return initProvider(p, Provider{
Name: "google",
Endpoint: google.Endpoint,
RedirectURL: p.SiteURL + "/auth/google",
RedirectURL: p.RemarkURL + "/auth/google",
Scopes: []string{"https://www.googleapis.com/auth/userinfo.email"},
InfoURL: "https://www.googleapis.com/oauth2/v3/userinfo",
FilesystemStore: p.SessionStore,
@@ -39,7 +39,7 @@ func NewGithub(p Params) *Provider {
return initProvider(p, Provider{
Name: "github",
Endpoint: github.Endpoint,
RedirectURL: p.SiteURL + "/auth/github",
RedirectURL: p.RemarkURL + "/auth/github",
Scopes: []string{"user:email"},
InfoURL: "https://api.github.com/user",
FilesystemStore: p.SessionStore,
+26 -27
View File
@@ -150,19 +150,19 @@ func (s *Server) createCommentCtrl(w http.ResponseWriter, r *http.Request) {
s.respCache.Flush()
render.Status(r, http.StatusAccepted)
render.JSON(w, r, JSON{"id": id, "url": comment.Locator.URL})
render.JSON(w, r, JSON{"id": id, "loc": comment.Locator})
}
// DELETE /comment/{id}?url=post-url
// DELETE /comment/{id}?site=siteID&url=post-url
func (s *Server) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
log.Printf("[DEBUG] delete comment %s", id)
url := r.URL.Query().Get("url")
err := s.DataService.Delete(store.Locator{URL: url}, id)
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
err := s.DataService.Delete(locator, id)
if err != nil {
log.Printf("[WARN] can't delete comment, %s", err)
log.Printf("[WARN] can't delete comment %s %+v, %s", id, locator, err)
httpError(w, r, http.StatusInternalServerError, err, "can't delete comment")
return
}
@@ -170,13 +170,13 @@ func (s *Server) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
s.respCache.Flush()
render.Status(r, http.StatusOK)
render.JSON(w, r, JSON{"id": id, "url": url})
render.JSON(w, r, JSON{"id": id, "loc": locator})
}
// GET /find?url=post-url&format=tree&sort=-time
// GET /find?site=siteID&url=post-url&format=tree&sort=-time
func (s *Server) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
url := r.URL.Query().Get("url")
log.Printf("[DEBUG] get comments for %s", url)
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
log.Printf("[DEBUG] get comments for %+v", locator)
cacheKey := r.URL.String()
if comments, ok := s.respCache.Get(cacheKey); ok {
@@ -185,9 +185,9 @@ func (s *Server) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
return
}
comments, err := s.DataService.Find(store.Request{Locator: store.Locator{URL: url}, Sort: r.URL.Query().Get("sort")})
comments, err := s.DataService.Find(store.Request{Locator: locator, Sort: r.URL.Query().Get("sort")})
if err != nil {
log.Printf("[WARN] can't get comments for %s, %s", url, err)
log.Printf("[WARN] can't get comments for %+v, %s", locator, err)
httpError(w, r, http.StatusInternalServerError, err, "can't load comments comment")
return
}
@@ -201,7 +201,7 @@ func (s *Server) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
renderJSONWithHTML(w, r, comments)
}
// GET /last/{max}?url=abc
// GET /last/{max}?site=siteID
func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
max, err := strconv.Atoi(chi.URLParam(r, "max"))
@@ -216,7 +216,7 @@ func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
return
}
comments, err := s.DataService.Last(store.Locator{}, max)
comments, err := s.DataService.Last(store.Locator{SiteID: r.URL.Query().Get("site")}, max)
if err != nil {
log.Printf("[WARN] can't get last comments, %s", err)
httpError(w, r, http.StatusInternalServerError, err, "can't get last comments")
@@ -228,15 +228,15 @@ func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
renderJSONWithHTML(w, r, comments)
}
// GET /id/{id}?url=post-url
// GET /id/{id}?site=siteID&url=post-url
func (s *Server) commentByIDCtrl(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "id")
url := r.URL.Query().Get("url")
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
log.Printf("[DEBUG] get comments by id %s, %s", id, url)
log.Printf("[DEBUG] get comments by id %s, %+v", id, locator)
comment, err := s.DataService.GetByID(store.Locator{URL: url}, id)
comment, err := s.DataService.GetByID(locator, id)
if err != nil {
log.Printf("[WARN] can't get comment, %s", err)
httpError(w, r, http.StatusInternalServerError, err, "can't get comment by id")
@@ -246,7 +246,7 @@ func (s *Server) commentByIDCtrl(w http.ResponseWriter, r *http.Request) {
renderJSONWithHTML(w, r, comment)
}
// GET /comments?user=id
// GET /comments?site=siteID&user=id
func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
userID := r.URL.Query().Get("user")
@@ -260,7 +260,7 @@ func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
return
}
comments, err := s.DataService.GetByUser(store.Locator{}, userID)
comments, err := s.DataService.GetByUser(store.Locator{SiteID: r.URL.Query().Get("site")}, userID)
if err != nil {
log.Printf("[WARN] can't get comment, %s", err)
httpError(w, r, http.StatusBadRequest, err, "can't get comment by user id")
@@ -282,18 +282,18 @@ func (s *Server) userInfoCtrl(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, user)
}
// GET /count?url=post-url
// GET /count?site=siteID&url=post-url
func (s *Server) countCtrl(w http.ResponseWriter, r *http.Request) {
url := r.URL.Query().Get("url")
count, err := s.DataService.Count(store.Locator{URL: url})
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
count, err := s.DataService.Count(locator)
if err != nil {
httpError(w, r, http.StatusBadRequest, err, "can't get count")
return
}
render.JSON(w, r, JSON{"count": count, "url": url})
render.JSON(w, r, JSON{"count": count, "loc": locator})
}
// PUT /vote/{id}?url=post-url&vote=1
// PUT /vote/{id}?site=siteID&url=post-url&vote=1
func (s *Server) voteCtrl(w http.ResponseWriter, r *http.Request) {
user, err := auth.GetUserInfo(r)
@@ -301,14 +301,13 @@ func (s *Server) voteCtrl(w http.ResponseWriter, r *http.Request) {
httpError(w, r, http.StatusUnauthorized, err, "can't get user info")
return
}
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
id := chi.URLParam(r, "id")
log.Printf("[DEBUG] vote for comment %s", id)
url := r.URL.Query().Get("url")
vote := r.URL.Query().Get("vote") == "1"
comment, err := s.DataService.Vote(store.Locator{URL: url}, id, user.ID, vote)
comment, err := s.DataService.Vote(locator, id, user.ID, vote)
if err != nil {
log.Printf("[WARN] vote rejected for %s - %s, %s", user.ID, id, err)
httpError(w, r, http.StatusBadRequest, err, "can't vote for comment")
+98 -21
View File
@@ -21,7 +21,7 @@ import (
// is a bucket with ts:reference
// - blocking info sits in "block" bucket. Key is userID, value - ts
type BoltDB struct {
*bolt.DB
dbs map[string]*bolt.DB
}
const (
@@ -32,16 +32,24 @@ const (
userLimit = 100
)
// BoltSite defines single site param
type BoltSite struct {
FileName string
SiteID string
}
// NewBoltDB makes persistent boltdb-based store
func NewBoltDB(dbFile string) (*BoltDB, error) {
log.Printf("[INFO] bolt store, %s", dbFile)
result := BoltDB{}
db, err := bolt.Open(dbFile, 0600, &bolt.Options{Timeout: 5 * time.Second})
if err != nil {
return nil, errors.Wrapf(err, "failed to make boltdb for %s", dbFile)
func NewBoltDB(sites ...BoltSite) (*BoltDB, error) {
log.Printf("[INFO] bolt store for sites %+v", sites)
result := BoltDB{dbs: make(map[string]*bolt.DB)}
for _, site := range sites {
db, err := bolt.Open(site.FileName, 0600, &bolt.Options{Timeout: 5 * time.Second})
if err != nil {
return nil, errors.Wrapf(err, "failed to make boltdb for %s", site.FileName)
}
result.dbs[site.SiteID] = db
}
result.DB = db
return &result, err
return &result, nil
}
// Create saves new comment to store
@@ -57,7 +65,11 @@ func (b *BoltDB) Create(comment Comment) (string, error) {
comment.Votes = make(map[string]bool)
comment = sanitizeComment(comment) // clear potentially dangerous js from all parts of comment
err := b.Update(func(tx *bolt.Tx) error {
bdb, err := b.db(comment.Locator.SiteID)
if err != nil {
return "", err
}
err = bdb.Update(func(tx *bolt.Tx) error {
bucket, e := tx.CreateBucketIfNotExists([]byte(comment.Locator.URL)) // bucket per post url
if e != nil {
return errors.Wrapf(e, "can't make or open bucket", comment.Locator.URL)
@@ -112,7 +124,12 @@ func (b *BoltDB) Create(comment Comment) (string, error) {
// Delete removes comment locator from the store
func (b *BoltDB) Delete(locator Locator, commentID string) error {
return b.Update(func(tx *bolt.Tx) error {
bdb, err := b.db(locator.SiteID)
if err != nil {
return err
}
return bdb.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(locator.URL))
if bucket == nil {
return errors.Errorf("no bucket %s in store", locator.URL)
@@ -139,7 +156,12 @@ func (b *BoltDB) Delete(locator Locator, commentID string) error {
func (b *BoltDB) Find(request Request) ([]Comment, error) {
res := []Comment{}
err := b.View(func(tx *bolt.Tx) error {
bdb, err := b.db(request.Locator.SiteID)
if err != nil {
return nil, err
}
err = bdb.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(request.Locator.URL))
if bucket == nil {
return errors.Errorf("no bucket %s in store", request.Locator.URL)
@@ -181,7 +203,12 @@ func (b *BoltDB) Find(request Request) ([]Comment, error) {
// GetByID returns comment by id across posts
func (b *BoltDB) GetByID(locator Locator, commentID string) (comment Comment, err error) {
err = b.View(func(tx *bolt.Tx) error {
bdb, err := b.db(locator.SiteID)
if err != nil {
return comment, err
}
err = bdb.View(func(tx *bolt.Tx) error {
lastBucket := tx.Bucket([]byte(lastBucketName))
if lastBucket == nil {
@@ -224,7 +251,12 @@ func (b *BoltDB) Last(locator Locator, max int) (result []Comment, err error) {
max = lastLimit
}
err = b.View(func(tx *bolt.Tx) error {
bdb, err := b.db(locator.SiteID)
if err != nil {
return nil, err
}
err = bdb.View(func(tx *bolt.Tx) error {
lastBucket := tx.Bucket([]byte(lastBucketName))
if lastBucket == nil {
return errors.Errorf("no bucket %s in store", lastBucketName)
@@ -264,7 +296,13 @@ func (b *BoltDB) Last(locator Locator, max int) (result []Comment, err error) {
// Count returns number of comments for locator
func (b *BoltDB) Count(locator Locator) (count int, err error) {
err = b.View(func(tx *bolt.Tx) error {
bdb, err := b.db(locator.SiteID)
if err != nil {
return 0, err
}
err = bdb.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(locator.URL))
if bucket == nil {
return errors.Errorf("no bucket %s in store", locator.URL)
@@ -278,7 +316,13 @@ 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 {
return b.Update(func(tx *bolt.Tx) error {
bdb, err := b.db(locator.SiteID)
if err != nil {
return err
}
return bdb.Update(func(tx *bolt.Tx) error {
bucket, e := tx.CreateBucketIfNotExists([]byte(blocksBucketName))
if e != nil {
@@ -301,7 +345,13 @@ func (b *BoltDB) SetBlock(locator Locator, userID string, status bool) error {
// IsBlocked checks if user blocked
func (b *BoltDB) IsBlocked(locator Locator, userID string) (result bool) {
_ = b.View(func(tx *bolt.Tx) error {
bdb, err := b.db(locator.SiteID)
if err != nil {
return false
}
_ = bdb.View(func(tx *bolt.Tx) error {
result = false
bucket := tx.Bucket([]byte(blocksBucketName))
if bucket != nil && bucket.Get([]byte(userID)) != nil {
@@ -314,7 +364,13 @@ func (b *BoltDB) IsBlocked(locator Locator, userID string) (result bool) {
// List returns list of buckets, which is list of all commented posts
func (b BoltDB) List(locator Locator) (result []string, err error) {
err = b.View(func(tx *bolt.Tx) error {
bdb, err := b.db(locator.SiteID)
if err != nil {
return nil, err
}
err = bdb.View(func(tx *bolt.Tx) error {
return tx.ForEach(func(name []byte, _ *bolt.Bucket) error {
if string(name) != lastBucketName && string(name) != userBucketName {
result = append(result, string(name))
@@ -332,8 +388,12 @@ func (b *BoltDB) GetByUser(locator Locator, userID string) (comments []Comment,
comments = []Comment{}
commentRefs := []string{}
bdb, err := b.db(locator.SiteID)
if err != nil {
return nil, err
}
// get list of references to comments
err = b.View(func(tx *bolt.Tx) error {
err = bdb.View(func(tx *bolt.Tx) error {
userBucket := tx.Bucket([]byte(userBucketName))
if userBucket == nil {
return errors.Errorf("no bucket %s in store", userBucketName)
@@ -375,7 +435,12 @@ func (b *BoltDB) GetByUser(locator Locator, userID string) (comments []Comment,
// Get for locator.URL and commentID string
func (b *BoltDB) Get(locator Locator, commentID string) (comment Comment, err error) {
err = b.View(func(tx *bolt.Tx) error {
bdb, err := b.db(locator.SiteID)
if err != nil {
return comment, err
}
err = bdb.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(locator.URL))
if bucket == nil {
return errors.Errorf("no bucket %s in store", locator.URL)
@@ -406,7 +471,12 @@ func (b *BoltDB) Put(locator Locator, comment Comment) error {
comment.User = curComment.User
}
return b.Update(func(tx *bolt.Tx) error {
bdb, err := b.db(locator.SiteID)
if err != nil {
return err
}
return bdb.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(locator.URL))
if bucket == nil {
return errors.Errorf("no bucket %s in store", locator.URL)
@@ -424,6 +494,13 @@ func (b *BoltDB) Put(locator Locator, comment Comment) error {
})
}
func (b *BoltDB) db(siteID string) (*bolt.DB, error) {
if res, ok := b.dbs[siteID]; ok {
return res, nil
}
return nil, errors.Errorf("site %s not found", siteID)
}
// ref represents key:value pair for extra, index-only buckets
type ref struct {
key string
+22 -18
View File
@@ -15,7 +15,7 @@ func TestBoltDB_CreateAndFind(t *testing.T) {
defer os.Remove(testDb)
b = prep(t)
res, err := b.Find(Request{Locator: Locator{URL: "https://radio-t.com"}})
res, err := b.Find(Request{Locator: Locator{URL: "https://radio-t.com", SiteID: "radio-t"}})
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,7 +27,7 @@ func TestBoltDB_Delete(t *testing.T) {
defer os.Remove(testDb)
b := prep(t)
loc := Locator{URL: "https://radio-t.com"}
loc := Locator{URL: "https://radio-t.com", SiteID: "radio-t"}
res, err := b.Find(Request{Locator: loc})
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
@@ -49,15 +49,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"}})
res, err := b.Find(Request{Locator: Locator{URL: "https://radio-t.com", SiteID: "radio-t"}})
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
comment, err := b.GetByID(Locator{URL: "https://radio-t.com"}, res[1].ID)
comment, err := b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "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"}, "1234567")
comment, err = b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, "1234567")
assert.NotNil(t, err)
}
@@ -65,12 +65,12 @@ func TestBoltDB_Last(t *testing.T) {
defer os.Remove(testDb)
b := prep(t)
res, err := b.Last(Locator{URL: "https://radio-t.com"}, 0)
res, err := b.Last(Locator{URL: "https://radio-t.com", SiteID: "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"}, 1)
res, err = b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 1)
assert.Nil(t, err)
assert.Equal(t, 1, len(res))
assert.Equal(t, "some text2", res[0].Text)
@@ -80,7 +80,7 @@ func TestBoltDB_Count(t *testing.T) {
defer os.Remove(testDb)
b := prep(t)
c, err := b.Count(Locator{URL: "https://radio-t.com"})
c, err := b.Count(Locator{URL: "https://radio-t.com", SiteID: "radio-t"})
assert.Nil(t, err)
assert.Equal(t, 2, c)
}
@@ -89,15 +89,15 @@ func TestBoltDB_BlockUser(t *testing.T) {
defer os.Remove(testDb)
b := prep(t)
assert.False(t, b.IsBlocked(Locator{SiteID: "site1"}, "user1"), "nothing blocked")
assert.False(t, b.IsBlocked(Locator{SiteID: "radio-t"}, "user1"), "nothing blocked")
assert.NoError(t, b.SetBlock(Locator{SiteID: "site1"}, "user1", true))
assert.True(t, b.IsBlocked(Locator{SiteID: "site1"}, "user1"), "user1 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.False(t, b.IsBlocked(Locator{SiteID: "site1"}, "user2"), "user2 still unblocked")
assert.False(t, b.IsBlocked(Locator{SiteID: "radio-t"}, "user2"), "user2 still unblocked")
assert.NoError(t, b.SetBlock(Locator{SiteID: "site1"}, "user1", false))
assert.False(t, b.IsBlocked(Locator{SiteID: "site1"}, "user1"), "user1 unblocked")
assert.NoError(t, b.SetBlock(Locator{SiteID: "radio-t"}, "user1", false))
assert.False(t, b.IsBlocked(Locator{SiteID: "radio-t"}, "user1"), "user1 unblocked")
}
@@ -106,12 +106,16 @@ func TestBoltDB_List(t *testing.T) {
b := prep(t) // two comments for https://radio-t.com
// add one more for https://radio-t.com/2
comment := Comment{Text: `some text, <a href="http://radio-t.com">link</a>`, 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"}}
comment := Comment{
Text: `some text, <a href="http://radio-t.com">link</a>`,
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)
res, err := b.List(Locator{SiteID: "site1"})
res, err := b.List(Locator{SiteID: "radio-t"})
assert.Nil(t, err)
assert.Equal(t, []string{"https://radio-t.com", "https://radio-t.com/2"}, res)
}
@@ -130,7 +134,7 @@ func TestBoltDB_GetForUser(t *testing.T) {
func prep(t *testing.T) *BoltDB {
os.Remove(testDb)
b, err := NewBoltDB(testDb)
b, err := NewBoltDB(BoltSite{FileName: "/tmp/test-remark.db", SiteID: "radio-t"})
assert.Nil(t, err)
comment := Comment{
+9 -9
View File
@@ -11,22 +11,22 @@ 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"}, 0)
res, err := b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
t.Logf("%+v", res[0])
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
assert.Equal(t, 0, res[0].Score)
assert.Equal(t, map[string]bool{}, res[0].Votes)
c, err := b.Vote(Locator{URL: "https://radio-t.com"}, res[0].ID, "user1", true)
c, err := b.Vote(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID, "user1", true)
assert.Nil(t, err)
assert.Equal(t, 1, c.Score)
assert.Equal(t, map[string]bool{"user1": true}, c.Votes)
_, err = b.Vote(Locator{URL: "https://radio-t.com"}, res[0].ID, "user1", true)
_, 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"}, 0)
res, err = b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
assert.Equal(t, 1, res[0].Score)
@@ -36,22 +36,22 @@ 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"}, 0)
res, err := b.Last(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, 0)
t.Logf("%+v", res[0])
assert.Nil(t, err)
assert.Equal(t, 2, len(res))
assert.Equal(t, false, res[0].Pin)
err = b.SetPin(Locator{URL: "https://radio-t.com"}, res[0].ID, true)
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"}, res[0].ID)
c, err := b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID)
assert.Nil(t, err)
assert.Equal(t, true, c.Pin)
err = b.SetPin(Locator{URL: "https://radio-t.com"}, res[0].ID, false)
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"}, res[0].ID)
c, err = b.GetByID(Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, res[0].ID)
assert.Nil(t, err)
assert.Equal(t, false, c.Pin)
}
+1 -1
View File
@@ -1,6 +1,6 @@
### find request with tree
GET https://demo.remark42.com/api/v1/find?url=https://radio-t.com/p/2017/12/16/podcast-576/&sort=time&format=tree"
GET https://demo.remark42.com/api/v1/find?site=demo&url=https://radio-t.com/p/2017/12/16/podcast-576/&sort=time&format=tree"
### find request with plain
GET https://demo.remark42.com/api/v1/find?url=https://radio-t.com/p/2017/12/16/podcast-576/&sort=time&format=plain"