From 330131f390ca13e543050a52d46fbe715a9484b6 Mon Sep 17 00:00:00 2001 From: Adhityaa Chandrasekar Date: Sun, 23 Sep 2018 00:40:06 -0400 Subject: [PATCH] api,db: add comments count endpoint Closes https://gitlab.com/commento/commento-ce/issues/27 --- api/comment_count.go | 43 +++++++++++++++++++++++ api/comment_count_test.go | 54 +++++++++++++++++++++++++++++ api/page.go | 1 + api/page_get.go | 5 +-- api/page_update.go | 2 ++ db/20180923002745-comment-count.sql | 15 ++++++++ 6 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 api/comment_count.go create mode 100644 api/comment_count_test.go create mode 100644 db/20180923002745-comment-count.sql diff --git a/api/comment_count.go b/api/comment_count.go new file mode 100644 index 0000000..7c54959 --- /dev/null +++ b/api/comment_count.go @@ -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}) +} diff --git a/api/comment_count_test.go b/api/comment_count_test.go new file mode 100644 index 0000000..dac31ac --- /dev/null +++ b/api/comment_count_test.go @@ -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 + } +} diff --git a/api/page.go b/api/page.go index 01bc661..9ab9641 100644 --- a/api/page.go +++ b/api/page.go @@ -6,4 +6,5 @@ type page struct { Domain string `json:"domain"` Path string `json:"path"` IsLocked bool `json:"isLocked"` + CommentCount int `json:"commentCount"` } diff --git a/api/page_get.go b/api/page_get.go index d76eda2..c62d3d1 100644 --- a/api/page_get.go +++ b/api/page_get.go @@ -11,19 +11,20 @@ func pageGet(domain string, path string) (page, error) { } statement := ` - SELECT isLocked + SELECT isLocked, commentCount FROM pages WHERE domain=$1 AND path=$2; ` row := db.QueryRow(statement, domain, 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 there haven't been any comments, there won't be a record for this // page. The sane thing to do is return defaults. // TODO: the defaults are hard-coded in two places: here and the schema p.IsLocked = false + p.CommentCount = 0 } else { logger.Errorf("error scanning page: %v", err) return page{}, errorInternal diff --git a/api/page_update.go b/api/page_update.go index 2a50165..c74bfb8 100644 --- a/api/page_update.go +++ b/api/page_update.go @@ -9,6 +9,8 @@ func pageUpdate(p page) error { return errorMissingField } + // fields to not update: + // commentCount statement := ` INSERT INTO pages (domain, path, isLocked) diff --git a/db/20180923002745-comment-count.sql b/db/20180923002745-comment-count.sql new file mode 100644 index 0000000..87a83b5 --- /dev/null +++ b/db/20180923002745-comment-count.sql @@ -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();