Skip to content

Commit 4cd4ebe

Browse files
committed
collect /find Info for tree and plain types consistently
MakeTree calculated Info locally for historical reasons, and the results were consistent with the dataService.Info call but calculated differently. That change fixes that, ensuring that Info is requested in the same manner.
1 parent 6269c19 commit 4cd4ebe

File tree

8 files changed

+75
-91
lines changed

8 files changed

+75
-91
lines changed

backend/app/rest/api/admin_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ func TestAdmin_ReadOnlyNoComments(t *testing.T) {
530530
err = json.Unmarshal([]byte(res), &comments)
531531
assert.NoError(t, err)
532532
assert.Equal(t, 0, len(comments.Comments), "should have 0 comments")
533-
assert.False(t, comments.Info.ReadOnly, "different from the plain format, should be fixed")
533+
assert.True(t, comments.Info.ReadOnly)
534534
t.Logf("%+v", comments)
535535
}
536536

backend/app/rest/api/rest.go

+5
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ type commentsWithInfo struct {
9898
Info store.PostInfo `json:"info,omitempty"`
9999
}
100100

101+
type treeWithInfo struct {
102+
*service.Tree
103+
Info store.PostInfo `json:"info,omitempty"`
104+
}
105+
101106
// Run the lister and request's router, activate rest server
102107
func (s *Rest) Run(address string, port int) {
103108
if address == "*" {

backend/app/rest/api/rest_public.go

+27-11
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ type pubStore interface {
5050

5151
// GET /find?site=siteID&url=post-url&format=[tree|plain]&sort=[+/-time|+/-score|+/-controversy]&view=[user|all]&since=unix_ts_msec
5252
// find comments for given post. Returns in tree or plain formats, sorted
53+
//
54+
// When `url` parameter is not set (e.g. request is for site-wide comments), does not return deleted comments.
5355
func (s *public) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
5456
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
5557
sort := r.URL.Query().Get("sort")
@@ -77,22 +79,36 @@ func (s *public) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
7779
comments = []store.Comment{} // error should clear comments and continue for post info
7880
}
7981
comments = s.applyView(comments, view)
82+
83+
var commentsInfo store.PostInfo
84+
if info, ee := s.dataService.Info(locator, s.readOnlyAge); ee == nil {
85+
commentsInfo = info
86+
}
87+
88+
if !since.IsZero() { // if since is set, number of comments can be different from total in the DB
89+
commentsInfo.Count = 0
90+
for _, c := range comments {
91+
if !c.Deleted {
92+
commentsInfo.Count++
93+
}
94+
}
95+
}
96+
97+
// post might be readonly without any comments, Info call will fail then and ReadOnly flag should be checked separately
98+
if !commentsInfo.ReadOnly && locator.URL != "" && s.dataService.IsReadOnly(locator) {
99+
commentsInfo.ReadOnly = true
100+
}
101+
80102
var b []byte
81103
switch format {
82104
case "tree":
83-
tree := service.MakeTree(comments, sort, s.readOnlyAge)
84-
if tree.Nodes == nil { // eliminate json nil serialization
85-
tree.Nodes = []*service.Node{}
86-
}
87-
if s.dataService.IsReadOnly(locator) {
88-
tree.Info.ReadOnly = true
105+
withInfo := treeWithInfo{Tree: service.MakeTree(comments, sort), Info: commentsInfo}
106+
if withInfo.Nodes == nil { // eliminate json nil serialization
107+
withInfo.Nodes = []*service.Node{}
89108
}
90-
b, e = encodeJSONWithHTML(tree)
109+
b, e = encodeJSONWithHTML(withInfo)
91110
default:
92-
withInfo := commentsWithInfo{Comments: comments}
93-
if info, ee := s.dataService.Info(locator, s.readOnlyAge); ee == nil {
94-
withInfo.Info = info
95-
}
111+
withInfo := commentsWithInfo{Comments: comments, Info: commentsInfo}
96112
b, e = encodeJSONWithHTML(withInfo)
97113
}
98114
return b, e

backend/app/rest/api/rest_public_test.go

+16-16
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ func TestRest_Find(t *testing.T) {
229229
assert.Equal(t, id2, comments.Comments[0].ID)
230230

231231
// get in tree mode
232-
tree := service.Tree{}
232+
tree := treeWithInfo{}
233233
res, code = get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah1&format=tree")
234234
assert.Equal(t, http.StatusOK, code)
235235
err = json.Unmarshal([]byte(res), &tree)
@@ -255,7 +255,7 @@ func TestRest_FindAge(t *testing.T) {
255255
_, err = srv.DataService.Create(c2)
256256
require.NoError(t, err)
257257

258-
tree := service.Tree{}
258+
tree := treeWithInfo{}
259259

260260
res, code := get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah1&format=tree")
261261
assert.Equal(t, http.StatusOK, code)
@@ -298,15 +298,15 @@ func TestRest_FindReadOnly(t *testing.T) {
298298
require.NoError(t, err)
299299
require.NoError(t, resp.Body.Close())
300300

301-
tree := service.Tree{}
301+
tree := treeWithInfo{}
302302
res, code := get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah1&format=tree")
303303
assert.Equal(t, http.StatusOK, code)
304304
err = json.Unmarshal([]byte(res), &tree)
305305
require.NoError(t, err)
306306
assert.Equal(t, "https://radio-t.com/blah1", tree.Info.URL)
307307
assert.True(t, tree.Info.ReadOnly, "post is ro")
308308

309-
tree = service.Tree{}
309+
tree = treeWithInfo{}
310310
res, code = get(t, ts.URL+"/api/v1/find?site=remark42&url=https://radio-t.com/blah2&format=tree")
311311
assert.Equal(t, http.StatusOK, code)
312312
err = json.Unmarshal([]byte(res), &tree)
@@ -650,28 +650,28 @@ func TestPublic_FindCommentsCtrl_ConsistentCount(t *testing.T) {
650650
{"url=test-url&since=" + sinceTenSecondsAgo, fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
651651
{"since=" + sinceTS[0], fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
652652
{"url=test-url&since=" + sinceTS[0], fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
653-
{"since=" + sinceTS[1], fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
654-
{"url=test-url&since=" + sinceTS[1], fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
655-
{"since=" + sinceTS[4], fmt.Sprintf(`"info":{"count":7,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
656-
{"url=test-url&since=" + sinceTS[4], fmt.Sprintf(`"info":{"url":"test-url","count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
657-
{"format=tree", `"info":{"url":"test-url","count":7`},
653+
{"since=" + sinceTS[1], fmt.Sprintf(`"info":{"count":6,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
654+
{"url=test-url&since=" + sinceTS[1], fmt.Sprintf(`"info":{"url":"test-url","count":5,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
655+
{"since=" + sinceTS[4], fmt.Sprintf(`"info":{"count":3,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
656+
{"url=test-url&since=" + sinceTS[4], fmt.Sprintf(`"info":{"url":"test-url","count":2,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
657+
{"format=tree", `"info":{"count":7`},
658658
{"format=tree&url=test-url", `"info":{"url":"test-url","count":6`},
659-
{"format=tree&sort=+time", `"info":{"url":"test-url","count":7`},
659+
{"format=tree&sort=+time", `"info":{"count":7`},
660660
{"format=tree&url=test-url&sort=+time", `"info":{"url":"test-url","count":6`},
661-
{"format=tree&sort=-score", `"info":{"url":"test-url","count":7`},
661+
{"format=tree&sort=-score", `"info":{"count":7`},
662662
{"format=tree&url=test-url&sort=-score", `"info":{"url":"test-url","count":6`},
663663
{"sort=+time", fmt.Sprintf(`"score":-25,"vote":0,"time":%q}],"info":{"count":7`, formattedTS[8])},
664664
{"sort=-time", fmt.Sprintf(`"score":1,"vote":0,"time":%q}],"info":{"count":7`, formattedTS[0])},
665665
{"sort=+score", fmt.Sprintf(`"score":10,"vote":0,"time":%q}],"info":{"count":7`, formattedTS[2])},
666666
{"sort=+score&url=test-url", fmt.Sprintf(`"score":10,"vote":0,"time":%q}],"info":{"url":"test-url","count":6`, formattedTS[2])},
667667
{"sort=-score", fmt.Sprintf(`"score":-25,"vote":0,"time":%q}],"info":{"count":7`, formattedTS[8])},
668668
{"sort=-score&url=test-url", fmt.Sprintf(`"score":-2,"vote":0,"controversy":1.5874010519681994,"time":%q}],"info":{"url":"test-url","count":6`, formattedTS[6])},
669-
{"sort=-time&since=" + sinceTS[4], fmt.Sprintf(`"score":-1,"vote":0,"controversy":2.924017738212866,"time":%q}],"info":{"count":7`, formattedTS[4])},
670-
{"sort=-score&since=" + sinceTS[3], fmt.Sprintf(`"score":-25,"vote":0,"time":%q}],"info":{"count":7`, formattedTS[8])},
671-
{"sort=-score&url=test-url&since=" + sinceTS[3], fmt.Sprintf(`"score":-2,"vote":0,"controversy":1.5874010519681994,"time":%q}],"info":{"url":"test-url","count":6`, formattedTS[6])},
672-
{"sort=+controversy&url=test-url&since=" + sinceTS[5], fmt.Sprintf(`"score":-2,"vote":0,"controversy":1.5874010519681994,"time":%q}],"info":{"url":"test-url","count":6`, formattedTS[6])},
669+
{"sort=-time&since=" + sinceTS[4], fmt.Sprintf(`"score":-1,"vote":0,"controversy":2.924017738212866,"time":%q}],"info":{"count":3`, formattedTS[4])},
670+
{"sort=-score&since=" + sinceTS[3], fmt.Sprintf(`"score":-25,"vote":0,"time":%q}],"info":{"count":4`, formattedTS[8])},
671+
{"sort=-score&url=test-url&since=" + sinceTS[3], fmt.Sprintf(`"score":-2,"vote":0,"controversy":1.5874010519681994,"time":%q}],"info":{"url":"test-url","count":3`, formattedTS[6])},
672+
{"sort=+controversy&url=test-url&since=" + sinceTS[5], fmt.Sprintf(`"score":-2,"vote":0,"controversy":1.5874010519681994,"time":%q}],"info":{"url":"test-url","count":1`, formattedTS[6])},
673673
// three comments of which last one deleted and doesn't have controversy so returned last
674-
{"sort=-controversy&url=test-url&since=" + sinceTS[5], fmt.Sprintf(`"score":0,"vote":0,"time":%q,"delete":true}],"info":{"url":"test-url","count":6`, formattedTS[7])},
674+
{"sort=-controversy&url=test-url&since=" + sinceTS[5], fmt.Sprintf(`"score":0,"vote":0,"time":%q,"delete":true}],"info":{"url":"test-url","count":1`, formattedTS[7])},
675675
}
676676

677677
for _, tc := range testCases {

backend/app/store/service/testdata/tree.json

+2-8
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,5 @@
242242
"time": "2017-12-25T19:47:22Z"
243243
}
244244
}
245-
],
246-
"info": {
247-
"url": "url",
248-
"count": 12,
249-
"first_time": "2017-12-25T19:46:01Z",
250-
"last_time": "2017-12-25T19:47:22Z"
251-
}
252-
}
245+
]
246+
}

backend/app/store/service/testdata/tree_del.json

+2-8
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,5 @@
176176
}
177177
}]
178178
}]
179-
}],
180-
"info": {
181-
"url": "url",
182-
"count": 8,
183-
"first_time": "2017-12-25T19:46:01Z",
184-
"last_time": "2017-12-25T19:47:05Z"
185-
}
186-
}
179+
}]
180+
}

backend/app/store/service/tree.go

+8-29
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import (
1010

1111
// Tree is formatter making tree from the list of comments
1212
type Tree struct {
13-
Nodes []*Node `json:"comments"`
14-
Info store.PostInfo `json:"info,omitempty"`
13+
Nodes []*Node `json:"comments"`
1514
}
1615

1716
// Node is a comment with optional replies
@@ -30,22 +29,12 @@ type recurData struct {
3029
}
3130

3231
// MakeTree gets unsorted list of comments and produces Tree
33-
// It will make store.PostInfo by itself and will mark Info.ReadOnly based on passed readOnlyAge
34-
// Tree maker is local and has no access to the data store. By this reason it has to make Info and won't be able
35-
// to handle store's read-only status. This status should be set by caller.
36-
func MakeTree(comments []store.Comment, sortType string, readOnlyAge int) *Tree {
32+
func MakeTree(comments []store.Comment, sortType string) *Tree {
3733
if len(comments) == 0 {
3834
return &Tree{}
3935
}
4036

41-
res := Tree{
42-
Info: store.PostInfo{
43-
URL: comments[0].Locator.URL,
44-
FirstTS: comments[0].Timestamp,
45-
LastTS: comments[0].Timestamp,
46-
},
47-
}
48-
res.Info.Count = len(res.filter(comments, func(c store.Comment) bool { return !c.Deleted }))
37+
res := Tree{}
4938

5039
topComments := res.filter(comments, func(c store.Comment) bool { return c.ParentID == "" })
5140

@@ -54,23 +43,12 @@ func MakeTree(comments []store.Comment, sortType string, readOnlyAge int) *Tree
5443
node := Node{Comment: rootComment}
5544

5645
rd := recurData{}
57-
commentsTree, tsModified, tsCreated := res.proc(comments, &node, &rd, rootComment.ID)
58-
// skip deleted with no sub-comments ar all sub-comments deleted
46+
commentsTree := res.proc(comments, &node, &rd, rootComment.ID)
47+
// skip deleted with no sub-comments and all sub-comments deleted
5948
if rootComment.Deleted && (len(commentsTree.Replies) == 0 || !rd.visible) {
6049
continue
6150
}
6251

63-
commentsTree.tsModified, commentsTree.tsCreated = tsModified, tsCreated
64-
if commentsTree.tsCreated.Before(res.Info.FirstTS) {
65-
res.Info.FirstTS = commentsTree.tsCreated
66-
}
67-
if commentsTree.tsModified.After(res.Info.LastTS) {
68-
res.Info.LastTS = commentsTree.tsModified
69-
}
70-
71-
res.Info.ReadOnly = readOnlyAge > 0 && !res.Info.FirstTS.IsZero() &&
72-
res.Info.FirstTS.AddDate(0, 0, readOnlyAge).Before(time.Now())
73-
7452
res.Nodes = append(res.Nodes, commentsTree)
7553
}
7654

@@ -79,7 +57,7 @@ func MakeTree(comments []store.Comment, sortType string, readOnlyAge int) *Tree
7957
}
8058

8159
// proc makes tree for one top-level comment recursively
82-
func (t *Tree) proc(comments []store.Comment, node *Node, rd *recurData, parentID string) (result *Node, modified, created time.Time) {
60+
func (t *Tree) proc(comments []store.Comment, node *Node, rd *recurData, parentID string) (result *Node) {
8361
if rd.tsModified.IsZero() || rd.tsCreated.IsZero() {
8462
rd.tsModified, rd.tsCreated = node.Comment.Timestamp, node.Comment.Timestamp
8563
}
@@ -106,7 +84,8 @@ func (t *Tree) proc(comments []store.Comment, node *Node, rd *recurData, parentI
10684
sort.Slice(node.Replies, func(i, j int) bool {
10785
return node.Replies[i].Comment.Timestamp.Before(node.Replies[j].Comment.Timestamp)
10886
})
109-
return node, rd.tsModified, rd.tsCreated
87+
node.tsModified, node.tsCreated = rd.tsModified, rd.tsCreated
88+
return node
11089
}
11190

11291
// filter returns comments for parentID

backend/app/store/service/tree_test.go

+14-18
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,15 @@ func TestMakeTree(t *testing.T) {
3737
{Locator: loc, ID: "611", ParentID: "61", Deleted: true},
3838
}
3939

40-
res := MakeTree(comments, "time", 0)
40+
res := MakeTree(comments, "time")
4141
resJSON, err := json.Marshal(&res)
4242
require.NoError(t, err)
4343

4444
expJSON := mustLoadJSONFile(t, "testdata/tree.json")
4545
assert.Equal(t, expJSON, resJSON)
46-
assert.Equal(t, store.PostInfo{URL: "url", Count: 12, FirstTS: ts(46, 1), LastTS: ts(47, 22)}, res.Info)
4746

48-
res = MakeTree([]store.Comment{}, "time", 0)
47+
res = MakeTree([]store.Comment{}, "time")
4948
assert.Equal(t, &Tree{}, res)
50-
51-
res = MakeTree(comments, "time", 10)
52-
assert.Equal(t, store.PostInfo{URL: "url", Count: 12, FirstTS: ts(46, 1), LastTS: ts(47, 22), ReadOnly: true}, res.Info)
5349
}
5450

5551
func TestMakeEmptySubtree(t *testing.T) {
@@ -79,7 +75,7 @@ func TestMakeEmptySubtree(t *testing.T) {
7975
{Locator: loc, ID: "3", Timestamp: ts(48, 1), Deleted: true}, // deleted top level
8076
}
8177

82-
res := MakeTree(comments, "time", 0)
78+
res := MakeTree(comments, "time")
8379
resJSON, err := json.Marshal(&res)
8480
require.NoError(t, err)
8581
t.Log(string(resJSON))
@@ -108,50 +104,50 @@ func TestTreeSortNodes(t *testing.T) {
108104
{ID: "5", Deleted: true, Timestamp: time.Date(2017, 12, 25, 19, 47, 22, 150, time.UTC)},
109105
}
110106

111-
res := MakeTree(comments, "+active", 0)
107+
res := MakeTree(comments, "+active")
112108
assert.Equal(t, "2", res.Nodes[0].Comment.ID)
113109
t.Log(res.Nodes[0].Comment.ID, res.Nodes[0].tsModified)
114110

115-
res = MakeTree(comments, "-active", 0)
111+
res = MakeTree(comments, "-active")
116112
t.Log(res.Nodes[0].Comment.ID, res.Nodes[0].tsModified)
117113
assert.Equal(t, "1", res.Nodes[0].Comment.ID)
118114

119-
res = MakeTree(comments, "+time", 0)
115+
res = MakeTree(comments, "+time")
120116
t.Log(res.Nodes[0].Comment.ID, res.Nodes[0].tsModified)
121117
assert.Equal(t, "1", res.Nodes[0].Comment.ID)
122118

123-
res = MakeTree(comments, "-time", 0)
119+
res = MakeTree(comments, "-time")
124120
assert.Equal(t, "6", res.Nodes[0].Comment.ID)
125121

126-
res = MakeTree(comments, "score", 0)
122+
res = MakeTree(comments, "score")
127123
assert.Equal(t, "4", res.Nodes[0].Comment.ID)
128124
assert.Equal(t, "3", res.Nodes[1].Comment.ID)
129125
assert.Equal(t, "6", res.Nodes[2].Comment.ID)
130126
assert.Equal(t, "1", res.Nodes[3].Comment.ID)
131127

132-
res = MakeTree(comments, "+score", 0)
128+
res = MakeTree(comments, "+score")
133129
assert.Equal(t, "4", res.Nodes[0].Comment.ID)
134130

135-
res = MakeTree(comments, "-score", 0)
131+
res = MakeTree(comments, "-score")
136132
assert.Equal(t, "2", res.Nodes[0].Comment.ID)
137133
assert.Equal(t, "1", res.Nodes[1].Comment.ID)
138134
assert.Equal(t, "3", res.Nodes[2].Comment.ID)
139135
assert.Equal(t, "6", res.Nodes[3].Comment.ID)
140136

141-
res = MakeTree(comments, "+controversy", 0)
137+
res = MakeTree(comments, "+controversy")
142138
assert.Equal(t, "3", res.Nodes[0].Comment.ID)
143139
assert.Equal(t, "6", res.Nodes[1].Comment.ID)
144140
assert.Equal(t, "2", res.Nodes[2].Comment.ID)
145141
assert.Equal(t, "4", res.Nodes[3].Comment.ID)
146142
assert.Equal(t, "1", res.Nodes[4].Comment.ID)
147143

148-
res = MakeTree(comments, "-controversy", 0)
144+
res = MakeTree(comments, "-controversy")
149145
assert.Equal(t, "1", res.Nodes[0].Comment.ID)
150146
assert.Equal(t, "4", res.Nodes[1].Comment.ID)
151147
assert.Equal(t, "2", res.Nodes[2].Comment.ID)
152148
assert.Equal(t, "3", res.Nodes[3].Comment.ID)
153149

154-
res = MakeTree(comments, "undefined", 0)
150+
res = MakeTree(comments, "undefined")
155151
t.Log(res.Nodes[0].Comment.ID, res.Nodes[0].tsModified)
156152
assert.Equal(t, "1", res.Nodes[0].Comment.ID)
157153
}
@@ -164,7 +160,7 @@ func BenchmarkTree(b *testing.B) {
164160
assert.NoError(b, err)
165161

166162
for i := 0; i < b.N; i++ {
167-
res := MakeTree(comments, "time", 0)
163+
res := MakeTree(comments, "time")
168164
assert.NotNil(b, res)
169165
}
170166
}

0 commit comments

Comments
 (0)