Skip to content

Commit 705f424

Browse files
authored
feat: datasource for gno.land/r/leon/hof integration with Gno.me (#3247)
This is quick initial PoC of a Gno.me integration idea.
1 parent c48219a commit 705f424

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package hof
2+
3+
import (
4+
"errors"
5+
6+
"gno.land/p/demo/avl"
7+
"gno.land/p/demo/ufmt"
8+
"gno.land/p/jeronimoalbi/datasource"
9+
)
10+
11+
func NewDatasource() Datasource {
12+
return Datasource{exhibition}
13+
}
14+
15+
type Datasource struct {
16+
exhibition *Exhibition
17+
}
18+
19+
func (ds Datasource) Size() int { return ds.exhibition.itemsSorted.Size() }
20+
21+
func (ds Datasource) Records(q datasource.Query) datasource.Iterator {
22+
return &iterator{
23+
exhibition: ds.exhibition,
24+
index: q.Offset,
25+
maxIndex: q.Offset + q.Count,
26+
}
27+
}
28+
29+
func (ds Datasource) Record(id string) (datasource.Record, error) {
30+
v, found := ds.exhibition.itemsSorted.Get(id)
31+
if !found {
32+
return nil, errors.New("realm submission not found")
33+
}
34+
return record{v.(*Item)}, nil
35+
}
36+
37+
type record struct {
38+
item *Item
39+
}
40+
41+
func (r record) ID() string { return r.item.id.String() }
42+
func (r record) String() string { return r.item.pkgpath }
43+
44+
func (r record) Fields() (datasource.Fields, error) {
45+
fields := avl.NewTree()
46+
fields.Set(
47+
"details",
48+
ufmt.Sprintf("Votes: ⏶ %d - ⏷ %d", r.item.upvote.Size(), r.item.downvote.Size()),
49+
)
50+
return fields, nil
51+
}
52+
53+
func (r record) Content() (string, error) {
54+
content := ufmt.Sprintf("# Submission #%d\n\n", int(r.item.id))
55+
content += r.item.Render(false)
56+
return content, nil
57+
}
58+
59+
type iterator struct {
60+
exhibition *Exhibition
61+
index, maxIndex int
62+
record *record
63+
}
64+
65+
func (it iterator) Record() datasource.Record { return it.record }
66+
func (it iterator) Err() error { return nil }
67+
68+
func (it *iterator) Next() bool {
69+
if it.index >= it.maxIndex || it.index >= it.exhibition.itemsSorted.Size() {
70+
return false
71+
}
72+
73+
_, v := it.exhibition.itemsSorted.GetByIndex(it.index)
74+
it.record = &record{v.(*Item)}
75+
it.index++
76+
return true
77+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package hof
2+
3+
import (
4+
"testing"
5+
6+
"gno.land/p/demo/avl"
7+
"gno.land/p/demo/uassert"
8+
"gno.land/p/demo/urequire"
9+
"gno.land/p/jeronimoalbi/datasource"
10+
)
11+
12+
var (
13+
_ datasource.Datasource = (*Datasource)(nil)
14+
_ datasource.Record = (*record)(nil)
15+
_ datasource.ContentRecord = (*record)(nil)
16+
_ datasource.Iterator = (*iterator)(nil)
17+
)
18+
19+
func TestDatasourceRecords(t *testing.T) {
20+
cases := []struct {
21+
name string
22+
items []*Item
23+
recordIDs []string
24+
options []datasource.QueryOption
25+
}{
26+
{
27+
name: "all items",
28+
items: []*Item{{id: 1}, {id: 2}, {id: 3}},
29+
recordIDs: []string{"0000001", "0000002", "0000003"},
30+
},
31+
{
32+
name: "with offset",
33+
items: []*Item{{id: 1}, {id: 2}, {id: 3}},
34+
recordIDs: []string{"0000002", "0000003"},
35+
options: []datasource.QueryOption{datasource.WithOffset(1)},
36+
},
37+
{
38+
name: "with count",
39+
items: []*Item{{id: 1}, {id: 2}, {id: 3}},
40+
recordIDs: []string{"0000001", "0000002"},
41+
options: []datasource.QueryOption{datasource.WithCount(2)},
42+
},
43+
{
44+
name: "with offset and count",
45+
items: []*Item{{id: 1}, {id: 2}, {id: 3}},
46+
recordIDs: []string{"0000002"},
47+
options: []datasource.QueryOption{
48+
datasource.WithOffset(1),
49+
datasource.WithCount(1),
50+
},
51+
},
52+
}
53+
54+
for _, tc := range cases {
55+
t.Run(tc.name, func(t *testing.T) {
56+
// Initialize a local instance of exhibition
57+
exhibition := &Exhibition{itemsSorted: avl.NewTree()}
58+
for _, item := range tc.items {
59+
exhibition.itemsSorted.Set(item.id.String(), item)
60+
}
61+
62+
// Get a records iterator
63+
ds := Datasource{exhibition}
64+
query := datasource.NewQuery(tc.options...)
65+
iter := ds.Records(query)
66+
67+
// Start asserting
68+
urequire.Equal(t, len(tc.items), ds.Size(), "datasource size")
69+
70+
var records []datasource.Record
71+
for iter.Next() {
72+
records = append(records, iter.Record())
73+
}
74+
urequire.Equal(t, len(tc.recordIDs), len(records), "record count")
75+
76+
for i, r := range records {
77+
uassert.Equal(t, tc.recordIDs[i], r.ID())
78+
}
79+
})
80+
}
81+
}
82+
83+
func TestDatasourceRecord(t *testing.T) {
84+
cases := []struct {
85+
name string
86+
items []*Item
87+
id string
88+
err string
89+
}{
90+
{
91+
name: "found",
92+
items: []*Item{{id: 1}, {id: 2}, {id: 3}},
93+
id: "0000001",
94+
},
95+
{
96+
name: "no found",
97+
items: []*Item{{id: 1}, {id: 2}, {id: 3}},
98+
id: "42",
99+
err: "realm submission not found",
100+
},
101+
}
102+
103+
for _, tc := range cases {
104+
t.Run(tc.name, func(t *testing.T) {
105+
// Initialize a local instance of exhibition
106+
exhibition := &Exhibition{itemsSorted: avl.NewTree()}
107+
for _, item := range tc.items {
108+
exhibition.itemsSorted.Set(item.id.String(), item)
109+
}
110+
111+
// Get a single record
112+
ds := Datasource{exhibition}
113+
r, err := ds.Record(tc.id)
114+
115+
// Start asserting
116+
if tc.err != "" {
117+
uassert.ErrorContains(t, err, tc.err)
118+
return
119+
}
120+
121+
urequire.NoError(t, err, "no error")
122+
urequire.NotEqual(t, nil, r, "record not nil")
123+
uassert.Equal(t, tc.id, r.ID())
124+
})
125+
}
126+
}
127+
128+
func TestItemRecord(t *testing.T) {
129+
pkgpath := "gno.land/r/demo/test"
130+
item := Item{
131+
id: 1,
132+
pkgpath: pkgpath,
133+
blockNum: 42,
134+
upvote: avl.NewTree(),
135+
downvote: avl.NewTree(),
136+
}
137+
item.downvote.Set("g1jg8mtutu9khhfwc4nxmuhcpftf0pajdhfvsqf5", struct{}{})
138+
item.upvote.Set("g1w4ek2u33ta047h6lta047h6lta047h6ldvdwpn", struct{}{})
139+
item.upvote.Set("g1w4ek2u3jta047h6lta047h6lta047h6l9huexc", struct{}{})
140+
141+
r := record{&item}
142+
143+
uassert.Equal(t, "0000001", r.ID())
144+
uassert.Equal(t, pkgpath, r.String())
145+
146+
fields, _ := r.Fields()
147+
details, found := fields.Get("details")
148+
urequire.True(t, found, "details field")
149+
uassert.Equal(t, "Votes: ⏶ 2 - ⏷ 1", details)
150+
151+
content, _ := r.Content()
152+
wantContent := "# Submission #1\n\n\n```\ngno.land/r/demo/test\n```\n\nby demo\n\n" +
153+
"[View realm](/r/demo/test)\n\nSubmitted at Block #42\n\n" +
154+
"#### [2👍](/r/leon/hof$help&func=Upvote&pkgpath=gno.land/r/demo/test) - " +
155+
"[1👎](/r/leon/hof$help&func=Downvote&pkgpath=gno.land/r/demo/test)\n\n"
156+
uassert.Equal(t, wantContent, content)
157+
}

0 commit comments

Comments
 (0)