Skip to content

Commit 36dbd9a

Browse files
authored
feat: add paging to messages (#730)
1 parent 8f45435 commit 36dbd9a

File tree

7 files changed

+247
-14
lines changed

7 files changed

+247
-14
lines changed

cmd/internal/http/params/query.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package params
22

33
import (
44
"net/http"
5+
"strconv"
56

67
"github.com/pkg/errors"
78
)
@@ -75,3 +76,18 @@ func QueryOptionalNetwork(r *http.Request) string {
7576

7677
return networks[0]
7778
}
79+
80+
// QueryDefaultInt verify presence and return value of parameter if empty return default value
81+
func QueryDefaultInt(r *http.Request, name string, defaultValue int32) (int32, error) {
82+
val := r.URL.Query()[name]
83+
if len(val) != 1 {
84+
return defaultValue, nil
85+
}
86+
87+
i64, err := strconv.ParseInt(val[0], 10, 32)
88+
if err != nil {
89+
return 0, err
90+
}
91+
92+
return int32(i64), nil
93+
}

cmd/mailchain/http/handlers/messages.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func GetMessages(receivers map[string]mailbox.Receiver, inbox stores.State, cach
6666
}
6767
}
6868

69-
txs, err := inbox.GetTransactions(req.Protocol, req.Network, req.addressBytes)
69+
txs, err := inbox.GetTransactions(req.Protocol, req.Network, req.addressBytes, req.Skip, req.Limit)
7070
if err != nil {
7171
errs.JSONWriter(w, r, http.StatusInternalServerError, errors.WithStack(err), logger)
7272
return
@@ -206,6 +206,20 @@ type GetMessagesRequest struct {
206206
// default: false
207207
Fetch bool `json:"fetch"`
208208

209+
// Limit the maximum number of message transactions that will be returned. Used for paging.
210+
//
211+
// in: query
212+
// example: 15
213+
// default: 15
214+
Limit int32 `json:"limit"`
215+
216+
// Skip this number of transactions before returning messages. Used for paging.
217+
//
218+
// in: query
219+
// example: 0
220+
// default: 0
221+
Skip int32 `json:"skip"`
222+
209223
addressBytes []byte
210224
}
211225

@@ -231,11 +245,23 @@ func parseGetMessagesRequest(r *http.Request) (*GetMessagesRequest, error) {
231245
return nil, err
232246
}
233247

248+
skip, err := params.QueryDefaultInt(r, "skip", 0)
249+
if err != nil {
250+
return nil, err
251+
}
252+
253+
limit, err := params.QueryDefaultInt(r, "limit", 15)
254+
if err != nil {
255+
return nil, err
256+
}
257+
234258
res := &GetMessagesRequest{
235259
Address: addr,
236260
addressBytes: addressBytes,
237261
Network: network,
238262
Protocol: protocol,
263+
Skip: skip,
264+
Limit: limit,
239265
}
240266

241267
fetch := r.URL.Query()["fetch"]

cmd/mailchain/http/handlers/messages_test.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func Test_GetMessages(t *testing.T) {
6262
args{
6363
inbox: func() stores.State {
6464
stateMock := statemock.NewMockState(mockCtrl)
65-
stateMock.EXPECT().GetTransactions("ethereum", "mainnet", addressingtest.EthereumCharlotte).Times(1)
65+
stateMock.EXPECT().GetTransactions("ethereum", "mainnet", addressingtest.EthereumCharlotte, int32(0), int32(15)).Times(1)
6666
return stateMock
6767
}(),
6868
ks: func() keystore.Store {
@@ -86,7 +86,7 @@ func Test_GetMessages(t *testing.T) {
8686
inbox: func() stores.State {
8787
inbox := statemock.NewMockState(mockCtrl)
8888
inbox.EXPECT().GetReadStatus(mail.ID{71, 236, 160, 17, 227, 43, 82, 199, 16, 5, 173, 138, 143, 117, 225, 180, 76, 146, 201, 159, 209, 46, 67, 188, 207, 229, 113, 227, 194, 209, 61, 46, 154, 130, 106, 85, 15, 95, 246, 59, 36, 122, 244, 113}).Return(false, nil).Times(1)
89-
inbox.EXPECT().GetTransactions("ethereum", "mainnet", addressingtest.EthereumCharlotte).Return([]stores.Transaction{
89+
inbox.EXPECT().GetTransactions("ethereum", "mainnet", addressingtest.EthereumCharlotte, int32(0), int32(15)).Return([]stores.Transaction{
9090
{
9191
BlockNumber: 100,
9292
Hash: []byte{0x01, 0x02, 0x03},
@@ -131,7 +131,7 @@ func Test_GetMessages(t *testing.T) {
131131
inbox: func() stores.State {
132132
inbox := statemock.NewMockState(mockCtrl)
133133
inbox.EXPECT().GetReadStatus(mail.ID{71, 236, 160, 17, 227, 43, 82, 199, 16, 5, 173, 138, 143, 117, 225, 180, 76, 146, 201, 159, 209, 46, 67, 188, 207, 229, 113, 227, 194, 209, 61, 46, 154, 130, 106, 85, 15, 95, 246, 59, 36, 122, 244, 113}).Return(false, nil).Times(1)
134-
inbox.EXPECT().GetTransactions("ethereum", "mainnet", addressingtest.EthereumCharlotte).Return([]stores.Transaction{
134+
inbox.EXPECT().GetTransactions("ethereum", "mainnet", addressingtest.EthereumCharlotte, int32(0), int32(15)).Return([]stores.Transaction{
135135
{
136136
BlockNumber: 100,
137137
Hash: []byte{0x01, 0x02, 0x03},
@@ -171,7 +171,7 @@ func Test_GetMessages(t *testing.T) {
171171
inbox: func() stores.State {
172172
inbox := statemock.NewMockState(mockCtrl)
173173
inbox.EXPECT().GetReadStatus(mail.ID{71, 236, 160, 17, 227, 43, 82, 199, 16, 5, 173, 138, 143, 117, 225, 180, 76, 146, 201, 159, 209, 46, 67, 188, 207, 229, 113, 227, 194, 209, 61, 46, 154, 130, 106, 85, 15, 95, 246, 59, 36, 122, 244, 113}).Return(false, nil).Times(1)
174-
inbox.EXPECT().GetTransactions("ethereum", "mainnet", addressingtest.EthereumCharlotte).Return([]stores.Transaction{
174+
inbox.EXPECT().GetTransactions("ethereum", "mainnet", addressingtest.EthereumCharlotte, int32(0), int32(15)).Return([]stores.Transaction{
175175
{
176176
BlockNumber: 100,
177177
Hash: []byte{0x01, 0x02, 0x03},
@@ -219,7 +219,7 @@ func Test_GetMessages(t *testing.T) {
219219
args{
220220
inbox: func() stores.State {
221221
inbox := statemock.NewMockState(mockCtrl)
222-
inbox.EXPECT().GetTransactions("ethereum", "mainnet", addressingtest.EthereumCharlotte).Return(nil, errors.New("internal error"))
222+
inbox.EXPECT().GetTransactions("ethereum", "mainnet", addressingtest.EthereumCharlotte, int32(0), int32(15)).Return(nil, errors.New("internal error"))
223223
return inbox
224224
}(),
225225
ks: func() keystore.Store {
@@ -306,6 +306,8 @@ func Test_parseGetMessagesRequest(t *testing.T) {
306306
Network: "mainnet",
307307
Protocol: "ethereum",
308308
Fetch: true,
309+
Limit: 15,
310+
Skip: 0,
309311
addressBytes: []byte{0x56, 0x2, 0xea, 0x95, 0x54, 0xb, 0xee, 0x46, 0xd0, 0x3b, 0xa3, 0x35, 0xee, 0xd6, 0xf4, 0x9d, 0x11, 0x7e, 0xab, 0x95, 0xc8, 0xab, 0x8b, 0x71, 0xba, 0xe2, 0xcd, 0xd1, 0xe5, 0x64, 0xa7, 0x61},
310312
},
311313
false,
@@ -323,10 +325,38 @@ func Test_parseGetMessagesRequest(t *testing.T) {
323325
Address: "0x5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761",
324326
Network: "mainnet",
325327
Protocol: "ethereum",
328+
Limit: 15,
329+
Skip: 0,
326330
addressBytes: []byte{0x56, 0x2, 0xea, 0x95, 0x54, 0xb, 0xee, 0x46, 0xd0, 0x3b, 0xa3, 0x35, 0xee, 0xd6, 0xf4, 0x9d, 0x11, 0x7e, 0xab, 0x95, 0xc8, 0xab, 0x8b, 0x71, 0xba, 0xe2, 0xcd, 0xd1, 0xe5, 0x64, 0xa7, 0x61},
327331
},
328332
false,
329333
},
334+
{
335+
"err-skip-value",
336+
args{
337+
map[string]string{
338+
"address": "0x5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761",
339+
"network": "mainnet",
340+
"protocol": "ethereum",
341+
"skip": "invalid",
342+
},
343+
},
344+
nil,
345+
true,
346+
},
347+
{
348+
"err-limit-value",
349+
args{
350+
map[string]string{
351+
"address": "0x5602ea95540bee46d03ba335eed6f49d117eab95c8ab8b71bae2cdd1e564a761",
352+
"network": "mainnet",
353+
"protocol": "ethereum",
354+
"limit": "invalid",
355+
},
356+
},
357+
nil,
358+
true,
359+
},
330360
{
331361
"err-invalid-protocol",
332362
args{

stores/bdbstore/transactions.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,29 @@ func (db *Database) PutTransaction(protocol, network string, address []byte, tx
2828
})
2929
}
3030

31-
func (db *Database) GetTransactions(protocol, network string, address []byte) ([]stores.Transaction, error) {
31+
func (db *Database) GetTransactions(protocol, network string, address []byte, skip, limit int32) ([]stores.Transaction, error) {
3232
var txs []stores.Transaction
3333

3434
err := db.db.View(func(txn *badger.Txn) error {
3535
prefix := []byte(getTransactionPrefixKey(protocol, network, address))
3636
opts := badger.DefaultIteratorOptions
3737
opts.PrefetchValues = false
38+
3839
it := txn.NewIterator(opts)
3940
defer it.Close()
4041

4142
for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
43+
if skip > 0 {
44+
skip--
45+
46+
continue
47+
}
48+
49+
if limit <= 0 {
50+
break
51+
}
52+
limit--
53+
4254
item := it.Item()
4355

4456
val, err := item.ValueCopy(nil)
@@ -52,6 +64,7 @@ func (db *Database) GetTransactions(protocol, network string, address []byte) ([
5264
}
5365

5466
txs = append(txs, tx)
67+
5568
}
5669
return nil
5770
})

0 commit comments

Comments
 (0)