api,db: add comments count endpoint
Closes https://gitlab.com/commento/commento-ce/issues/27
This commit is contained in:
parent
299649cea2
commit
330131f390
43
api/comment_count.go
Normal file
43
api/comment_count.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func commentCount(domain string, path string) (int, error) {
|
||||||
|
// path can be empty
|
||||||
|
if domain == "" {
|
||||||
|
return 0, errorMissingField
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := pageGet(domain, path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errorInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.CommentCount, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func commentCountHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
type request struct {
|
||||||
|
Domain *string `json:"domain"`
|
||||||
|
Path *string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var x request
|
||||||
|
if err := bodyUnmarshal(r, &x); err != nil {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
domain := domainStrip(*x.Domain)
|
||||||
|
path := *x.Path
|
||||||
|
|
||||||
|
count, err := commentCount(domain, path)
|
||||||
|
if err != nil {
|
||||||
|
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyMarshal(w, response{"success": true, "count": count})
|
||||||
|
}
|
54
api/comment_count_test.go
Normal file
54
api/comment_count_test.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommentCountBasics(t *testing.T) {
|
||||||
|
failTestOnError(t, setupTestEnv())
|
||||||
|
|
||||||
|
commenterHex, _ := commenterNew("test@example.com", "Test", "undefined", "http://example.com/photo.jpg", "google", "")
|
||||||
|
|
||||||
|
commentNew(commenterHex, "example.com", "/path.html", "root", "**foo**", "approved", time.Now().UTC())
|
||||||
|
commentNew(commenterHex, "example.com", "/path.html", "root", "**bar**", "approved", time.Now().UTC())
|
||||||
|
commentNew(commenterHex, "example.com", "/path.html", "root", "**baz**", "unapproved", time.Now().UTC())
|
||||||
|
|
||||||
|
count, err := commentCount("example.com", "/path.html")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error counting comments: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if count != 2 {
|
||||||
|
t.Errorf("expected count=2 got count=%d", count)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommentCountNewPage(t *testing.T) {
|
||||||
|
failTestOnError(t, setupTestEnv())
|
||||||
|
|
||||||
|
count, err := commentCount("example.com", "/path.html")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error counting comments: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if count != 0 {
|
||||||
|
t.Errorf("expected count=0 got count=%d", count)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommentCountEmpty(t *testing.T) {
|
||||||
|
if _, err := commentCount("example.com", ""); err != nil {
|
||||||
|
t.Errorf("unexpected error counting comments on empty path: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := commentCount("", ""); err == nil {
|
||||||
|
t.Errorf("expected error not found counting comments with empty everything")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
@ -6,4 +6,5 @@ type page struct {
|
|||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
IsLocked bool `json:"isLocked"`
|
IsLocked bool `json:"isLocked"`
|
||||||
|
CommentCount int `json:"commentCount"`
|
||||||
}
|
}
|
||||||
|
@ -11,19 +11,20 @@ func pageGet(domain string, path string) (page, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
statement := `
|
statement := `
|
||||||
SELECT isLocked
|
SELECT isLocked, commentCount
|
||||||
FROM pages
|
FROM pages
|
||||||
WHERE domain=$1 AND path=$2;
|
WHERE domain=$1 AND path=$2;
|
||||||
`
|
`
|
||||||
row := db.QueryRow(statement, domain, path)
|
row := db.QueryRow(statement, domain, path)
|
||||||
|
|
||||||
p := page{Domain: domain, Path: path}
|
p := page{Domain: domain, Path: path}
|
||||||
if err := row.Scan(&p.IsLocked); err != nil {
|
if err := row.Scan(&p.IsLocked, &p.CommentCount); err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
// If there haven't been any comments, there won't be a record for this
|
// If there haven't been any comments, there won't be a record for this
|
||||||
// page. The sane thing to do is return defaults.
|
// page. The sane thing to do is return defaults.
|
||||||
// TODO: the defaults are hard-coded in two places: here and the schema
|
// TODO: the defaults are hard-coded in two places: here and the schema
|
||||||
p.IsLocked = false
|
p.IsLocked = false
|
||||||
|
p.CommentCount = 0
|
||||||
} else {
|
} else {
|
||||||
logger.Errorf("error scanning page: %v", err)
|
logger.Errorf("error scanning page: %v", err)
|
||||||
return page{}, errorInternal
|
return page{}, errorInternal
|
||||||
|
@ -9,6 +9,8 @@ func pageUpdate(p page) error {
|
|||||||
return errorMissingField
|
return errorMissingField
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fields to not update:
|
||||||
|
// commentCount
|
||||||
statement := `
|
statement := `
|
||||||
INSERT INTO
|
INSERT INTO
|
||||||
pages (domain, path, isLocked)
|
pages (domain, path, isLocked)
|
||||||
|
15
db/20180923002745-comment-count.sql
Normal file
15
db/20180923002745-comment-count.sql
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
ALTER TABLE pages
|
||||||
|
ADD commentCount INTEGER NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION commentsInsertTriggerFunction() RETURNS TRIGGER AS $trigger$
|
||||||
|
BEGIN
|
||||||
|
UPDATE pages
|
||||||
|
SET commentCount = commentCount + 1
|
||||||
|
WHERE domain = new.domain AND path = new.path;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$trigger$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
CREATE TRIGGER commentsInsertTrigger AFTER INSERT ON comments
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE commentsInsertTriggerFunction();
|
Loading…
Reference in New Issue
Block a user