Skip to content

Commit dc232ae

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 7c8da5d commit dc232ae

File tree

7 files changed

+63
-93
lines changed

7 files changed

+63
-93
lines changed

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

+25-23
Original file line numberDiff line numberDiff line change
@@ -79,34 +79,36 @@ func (s *public) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
7979
comments = []store.Comment{} // error should clear comments and continue for post info
8080
}
8181
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+
82102
var b []byte
83103
switch format {
84104
case "tree":
85-
tree := service.MakeTree(comments, sort, s.readOnlyAge)
86-
if tree.Nodes == nil { // eliminate json nil serialization
87-
tree.Nodes = []*service.Node{}
88-
}
89-
if s.dataService.IsReadOnly(locator) {
90-
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{}
91108
}
92-
b, e = encodeJSONWithHTML(tree)
109+
b, e = encodeJSONWithHTML(withInfo)
93110
default:
94-
withInfo := commentsWithInfo{Comments: comments}
95-
if info, ee := s.dataService.Info(locator, s.readOnlyAge); ee == nil {
96-
withInfo.Info = info
97-
}
98-
if !since.IsZero() { // if since is set, number of comments can be different from total in the DB
99-
withInfo.Info.Count = 0
100-
for _, c := range comments {
101-
if !c.Deleted {
102-
withInfo.Info.Count++
103-
}
104-
}
105-
}
106-
// post might be readonly without any comments, Info call will fail then and ReadOnly flag should be checked separately
107-
if !withInfo.Info.ReadOnly && locator.URL != "" && s.dataService.IsReadOnly(locator) {
108-
withInfo.Info.ReadOnly = true
109-
}
111+
withInfo := commentsWithInfo{Comments: comments, Info: commentsInfo}
110112
b, e = encodeJSONWithHTML(withInfo)
111113
}
112114
return b, e

backend/app/rest/api/rest_public_test.go

+7-7
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)
@@ -654,11 +654,11 @@ func TestPublic_FindCommentsCtrl_ConsistentCount(t *testing.T) {
654654
{"url=test-url&since=" + sinceTS[1], fmt.Sprintf(`"info":{"url":"test-url","count":5,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[7])},
655655
{"since=" + sinceTS[4], fmt.Sprintf(`"info":{"count":3,"first_time":%q,"last_time":%q}`, formattedTS[0], formattedTS[8])},
656656
{"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":{"url":"test-url","count":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])},

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)