add comments per user
This commit is contained in:
@@ -98,6 +98,7 @@ Sort can be `time` or `score`. Supported sort order with prefix -/+, i.e. `-time
|
||||
|
||||
- `GET /api/v1/last/{max}` - get up to `{max}` last comments
|
||||
- `GET /api/v1/id/{id}` - get comment by `id`
|
||||
- `GET /api/v1/comments?user=id` - get comment by `user id`
|
||||
- `GET /api/v1/count?url=post-url` - get comment's count for `{url}`
|
||||
- `PUT /api/v1/vote/{id}?url=post-url&vote=1` - vote for comment. `vote`=1 will increase score, -1 decreases. _auth required_
|
||||
- `DELETE /api/v1/admin/comment/{id}?url=post-url` - delete comment by `id`. _auth and admin required_
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestStore_MakeTree(t *testing.T) {
|
||||
enc.SetIndent("", " ")
|
||||
err := enc.Encode(res)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expJSON, string(buf.Bytes()))
|
||||
assert.Equal(t, expJSON, buf.String())
|
||||
// t.Log(string(buf.Bytes()))
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ func (s *Server) Run() {
|
||||
router.Route("/api/v1", func(rapi chi.Router) {
|
||||
rapi.Get("/find", s.findCommentsCtrl)
|
||||
rapi.Get("/id/{id}", s.commentByIDCtrl)
|
||||
rapi.Get("/comments", s.findUserCommentsCtrl)
|
||||
rapi.Get("/last/{max}", s.lastCommentsCtrl)
|
||||
rapi.Get("/count", s.countCtrl)
|
||||
|
||||
@@ -219,6 +220,23 @@ func (s *Server) commentByIDCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
renderJSONWithHTML(w, r, comment)
|
||||
}
|
||||
|
||||
// GET /comments?user=id
|
||||
func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
userID := chi.URLParam(r, "user")
|
||||
|
||||
log.Printf("[DEBUG] get comments by userID %s", userID)
|
||||
|
||||
comment, err := s.Store.GetForUser(store.Locator{}, 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")
|
||||
return
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
renderJSONWithHTML(w, r, comment)
|
||||
}
|
||||
|
||||
// GET /user
|
||||
func (s *Server) userInfoCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
user, err := auth.GetUserInfo(r)
|
||||
|
||||
+62
-2
@@ -21,6 +21,7 @@ type BoltDB struct {
|
||||
|
||||
const (
|
||||
lastBucketName = "last"
|
||||
userBucketName = "users"
|
||||
blocksBucketPrefix = "block-"
|
||||
lastLimit = 1000
|
||||
)
|
||||
@@ -76,13 +77,34 @@ func (b *BoltDB) Create(comment Comment) (string, error) {
|
||||
if e != nil {
|
||||
return errors.Wrapf(e, "can't make bucket %s", lastBucketName)
|
||||
}
|
||||
|
||||
rv := refFromComment(comment)
|
||||
e = bucket.Put([]byte(rv.key), []byte(rv.value))
|
||||
if e != nil {
|
||||
return errors.Wrapf(e, "can't put reference %s to %s", rv.value, lastBucketName)
|
||||
}
|
||||
|
||||
// add reference to commentID to "users" bucket
|
||||
bucket, e = tx.CreateBucketIfNotExists([]byte(userBucketName))
|
||||
if e != nil {
|
||||
return errors.Wrapf(e, "can't make bucket %s", userBucketName)
|
||||
}
|
||||
|
||||
userRefs := []string{} // holds current comment refs for the user
|
||||
if data := bucket.Get([]byte(comment.User.ID)); data != nil {
|
||||
if err := json.Unmarshal(data, &userRefs); err != nil {
|
||||
return errors.Wrapf(e, "can't unmarshal comments for %s", comment.User.ID)
|
||||
}
|
||||
}
|
||||
userRefs = append(userRefs, rv.value)
|
||||
// serialize to json []byte for bolt and save
|
||||
jdata, jerr = json.Marshal(&userRefs)
|
||||
if jerr != nil {
|
||||
return errors.Wrapf(jerr, "can't marshal comment ids for user %s, comment %s", comment.User.ID, comment.ID)
|
||||
}
|
||||
|
||||
if err := bucket.Put([]byte(comment.User.ID), jdata); err != nil {
|
||||
return errors.Wrapf(err, "failed to put user comment %s", comment.ID)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -345,7 +367,7 @@ func (b BoltDB) List(locator Locator) (result []string, err error) {
|
||||
|
||||
err = b.View(func(tx *bolt.Tx) error {
|
||||
return tx.ForEach(func(name []byte, _ *bolt.Bucket) error {
|
||||
if string(name) != lastBucketName {
|
||||
if string(name) != lastBucketName && string(name) != userBucketName {
|
||||
result = append(result, string(name))
|
||||
}
|
||||
return nil
|
||||
@@ -383,6 +405,44 @@ func (b *BoltDB) SetPin(locator Locator, commentID string, status bool) error {
|
||||
})
|
||||
}
|
||||
|
||||
// GetForUser extracts all comments for given site and given userID
|
||||
// "users" bucket has pairs userID:[]commentID
|
||||
func (b *BoltDB) GetForUser(locator Locator, userID string) (comments []Comment, err error) {
|
||||
|
||||
comments = []Comment{}
|
||||
commentRefs := []string{}
|
||||
err = b.View(func(tx *bolt.Tx) error {
|
||||
userBucket := tx.Bucket([]byte(userBucketName))
|
||||
if userBucket == nil {
|
||||
return errors.Errorf("no bucket %s in store", userBucketName)
|
||||
}
|
||||
|
||||
uData := userBucket.Get([]byte(userID))
|
||||
if uData == nil {
|
||||
return errors.Errorf("no comments for user %s in store", userID)
|
||||
}
|
||||
if e := json.Unmarshal(uData, &commentRefs); e != nil {
|
||||
return errors.Wrap(e, "failed to unmarshal list of user's comments")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return comments, err
|
||||
}
|
||||
|
||||
for _, v := range commentRefs {
|
||||
url, commentID, e := ref{value: v}.parseValue()
|
||||
if e != nil {
|
||||
return comments, errors.Wrapf(e, "can't parse reference %s", v)
|
||||
}
|
||||
if c, e := b.Get(Locator{URL: url, SiteID: locator.SiteID}, commentID); e == nil {
|
||||
comments = append(comments, c)
|
||||
}
|
||||
}
|
||||
return comments, err
|
||||
}
|
||||
|
||||
func (b *BoltDB) bucketForBlock(locator Locator, userID string) []byte {
|
||||
return []byte(fmt.Sprintf("%s%s", blocksBucketPrefix, locator.SiteID))
|
||||
}
|
||||
|
||||
+23
-4
@@ -165,6 +165,15 @@ func TestBoltDB_Pin(t *testing.T) {
|
||||
assert.Equal(t, false, c.Pin)
|
||||
}
|
||||
|
||||
func TestBoltDB_GetForUser(t *testing.T) {
|
||||
defer os.Remove(testDb)
|
||||
b := prep(t)
|
||||
|
||||
res, err := b.GetForUser(Locator{SiteID: "radio-t"}, "user1")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, len(res))
|
||||
}
|
||||
|
||||
// makes new boltdb, put two records
|
||||
func prep(t *testing.T) *BoltDB {
|
||||
os.Remove(testDb)
|
||||
@@ -172,13 +181,23 @@ func prep(t *testing.T) *BoltDB {
|
||||
b, err := NewBoltDB(testDb)
|
||||
assert.Nil(t, err)
|
||||
|
||||
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", SiteID: "radio-t"}, User: User{ID: "user1", Name: "user name"}}
|
||||
comment := Comment{
|
||||
ID: "id-1",
|
||||
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", SiteID: "radio-t"},
|
||||
User: User{ID: "user1", Name: "user name"},
|
||||
}
|
||||
_, err = b.Create(comment)
|
||||
assert.Nil(t, err)
|
||||
|
||||
comment = Comment{Text: "some text2", Timestamp: time.Date(2017, 12, 20, 15, 18, 23, 0, time.Local),
|
||||
Locator: Locator{URL: "https://radio-t.com", SiteID: "radio-t"}, User: User{ID: "user1", Name: "user name"}}
|
||||
comment = Comment{
|
||||
ID: "id-2",
|
||||
Text: "some text2",
|
||||
Timestamp: time.Date(2017, 12, 20, 15, 18, 23, 0, time.Local),
|
||||
Locator: Locator{URL: "https://radio-t.com", SiteID: "radio-t"},
|
||||
User: User{ID: "user1", Name: "user name"},
|
||||
}
|
||||
_, err = b.Create(comment)
|
||||
assert.Nil(t, err)
|
||||
|
||||
|
||||
+1
-1
@@ -61,10 +61,10 @@ type Interface interface {
|
||||
Vote(locator Locator, commentID string, userID string, val bool) (Comment, error)
|
||||
Count(locator Locator) (int, error)
|
||||
List(locator Locator) ([]string, error)
|
||||
GetForUser(locator Locator, userID string) ([]Comment, error)
|
||||
|
||||
SetBlock(locator Locator, userID string, status bool) error
|
||||
IsBlocked(locator Locator, userID string) bool
|
||||
|
||||
SetPin(locator Locator, commentID string, status bool) error
|
||||
}
|
||||
|
||||
|
||||
@@ -36,5 +36,8 @@ GET http://remark.umputun.com/api/v1/user
|
||||
### get comment by id
|
||||
GET http://remark.umputun.com/api/v1/id/3665976683?url=https://radio-t.com/p/2017/12/16/podcast-576/
|
||||
|
||||
### get comment by user id
|
||||
GET http://remark.umputun.com/api/v1/api/v1/comments?user=umputun
|
||||
|
||||
### get counts
|
||||
GET http://remark.umputun.com/api/v1/count?url=https://radio-t.com/p/2017/12/16/podcast-576/
|
||||
Reference in New Issue
Block a user