add reverse iteration to pagination (#8875)
* WIP * add tests * add tests * fix lint * fix pagination * add proto message doc * fix filtered_pagination * fix test * cleanup * add reverse flag to pagination * changelog * Update client/flags/flags.go * Update CHANGELOG.md Co-authored-by: Alessio Treglia <alessio@tendermint.com> Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
parent
025d226099
commit
a78f777ea8
@ -41,6 +41,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* [\#8559](https://github.com/cosmos/cosmos-sdk/pull/8559) Added Protobuf compatible secp256r1 ECDSA signatures.
|
||||
* [\#8786](https://github.com/cosmos/cosmos-sdk/pull/8786) Enabled secp256r1 in x/auth.
|
||||
* (rosetta) [\#8729](https://github.com/cosmos/cosmos-sdk/pull/8729) Data API fully supports balance tracking. Construction API can now construct any message supported by the application.
|
||||
* [\#8754](https://github.com/cosmos/cosmos-sdk/pull/8875) Added support for reverse iteration to pagination.
|
||||
|
||||
### Client Breaking Changes
|
||||
|
||||
|
||||
@ -71,6 +71,7 @@ const (
|
||||
FlagTimeoutHeight = "timeout-height"
|
||||
FlagKeyAlgorithm = "algo"
|
||||
FlagFeeAccount = "fee-account"
|
||||
FlagReverse = "reverse"
|
||||
|
||||
// Tendermint logging flags
|
||||
FlagLogLevel = "log_level"
|
||||
@ -131,6 +132,7 @@ func AddPaginationFlagsToCmd(cmd *cobra.Command, query string) {
|
||||
cmd.Flags().Uint64(FlagOffset, 0, fmt.Sprintf("pagination offset of %s to query for", query))
|
||||
cmd.Flags().Uint64(FlagLimit, 100, fmt.Sprintf("pagination limit of %s to query for", query))
|
||||
cmd.Flags().Bool(FlagCountTotal, false, fmt.Sprintf("count total number of records in %s to query for", query))
|
||||
cmd.Flags().Bool(FlagReverse, false, "results are sorted in descending order")
|
||||
}
|
||||
|
||||
// GasSetting encapsulates the possible values passed through the --gas flag.
|
||||
|
||||
@ -51,6 +51,7 @@ func ReadPageRequest(flagSet *pflag.FlagSet) (*query.PageRequest, error) {
|
||||
limit, _ := flagSet.GetUint64(flags.FlagLimit)
|
||||
countTotal, _ := flagSet.GetBool(flags.FlagCountTotal)
|
||||
page, _ := flagSet.GetUint64(flags.FlagPage)
|
||||
reverse, _ := flagSet.GetBool(flags.FlagReverse)
|
||||
|
||||
if page > 1 && offset > 0 {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "page and offset cannot be used together")
|
||||
@ -65,5 +66,6 @@ func ReadPageRequest(flagSet *pflag.FlagSet) (*query.PageRequest, error) {
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
CountTotal: countTotal,
|
||||
Reverse: reverse,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -680,6 +680,7 @@ pagination. Ex:
|
||||
| `offset` | [uint64](#uint64) | | offset is a numeric offset that can be used when key is unavailable. It is less efficient than using key. Only one of offset or key should be set. |
|
||||
| `limit` | [uint64](#uint64) | | limit is the total number of results to be returned in the result page. If left empty it will default to a value to be set by each app. |
|
||||
| `count_total` | [bool](#bool) | | count_total is set to true to indicate that the result set should include a count of the total number of items available for pagination in UIs. count_total is only respected when offset is used. It is ignored when key is set. |
|
||||
| `reverse` | [bool](#bool) | | reverse is set to true indicates that, results to be returned in the descending order. |
|
||||
|
||||
|
||||
|
||||
|
||||
@ -30,6 +30,9 @@ message PageRequest {
|
||||
// count_total is only respected when offset is used. It is ignored when key
|
||||
// is set.
|
||||
bool count_total = 4;
|
||||
|
||||
// reverse is set to true indicates that, results to be returned in the descending order.
|
||||
bool reverse = 5;
|
||||
}
|
||||
|
||||
// PageResponse is to be embedded in gRPC response messages where the
|
||||
|
||||
@ -29,6 +29,7 @@ func FilteredPaginate(
|
||||
key := pageRequest.Key
|
||||
limit := pageRequest.Limit
|
||||
countTotal := pageRequest.CountTotal
|
||||
reverse := pageRequest.Reverse
|
||||
|
||||
if offset > 0 && key != nil {
|
||||
return nil, fmt.Errorf("invalid request, either offset or key is expected, got both")
|
||||
@ -42,7 +43,7 @@ func FilteredPaginate(
|
||||
}
|
||||
|
||||
if len(key) != 0 {
|
||||
iterator := prefixStore.Iterator(key, nil)
|
||||
iterator := getIterator(prefixStore, key, reverse)
|
||||
defer iterator.Close()
|
||||
|
||||
var numHits uint64
|
||||
@ -73,7 +74,7 @@ func FilteredPaginate(
|
||||
}, nil
|
||||
}
|
||||
|
||||
iterator := prefixStore.Iterator(nil, nil)
|
||||
iterator := getIterator(prefixStore, nil, reverse)
|
||||
defer iterator.Close()
|
||||
|
||||
end := offset + limit
|
||||
|
||||
@ -89,6 +89,87 @@ func (s *paginationTestSuite) TestFilteredPaginations() {
|
||||
s.Require().LessOrEqual(len(balances), 2)
|
||||
}
|
||||
|
||||
func (s *paginationTestSuite) TestReverseFilteredPaginations() {
|
||||
app, ctx, appCodec := setupTest()
|
||||
|
||||
var balances sdk.Coins
|
||||
for i := 0; i < numBalances; i++ {
|
||||
denom := fmt.Sprintf("foo%ddenom", i)
|
||||
balances = append(balances, sdk.NewInt64Coin(denom, 100))
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
denom := fmt.Sprintf("test%ddenom", i)
|
||||
balances = append(balances, sdk.NewInt64Coin(denom, 250))
|
||||
}
|
||||
|
||||
balances = balances.Sort()
|
||||
addr1 := sdk.AccAddress([]byte("addr1"))
|
||||
acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1)
|
||||
app.AccountKeeper.SetAccount(ctx, acc1)
|
||||
s.Require().NoError(simapp.FundAccount(app, ctx, addr1, balances))
|
||||
store := ctx.KVStore(app.GetKey(types.StoreKey))
|
||||
|
||||
// verify pagination with limit > total values
|
||||
pageReq := &query.PageRequest{Key: nil, Limit: 5, CountTotal: true, Reverse: true}
|
||||
balns, res, err := execFilterPaginate(store, pageReq, appCodec)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(res)
|
||||
s.Require().Equal(5, len(balns))
|
||||
|
||||
s.T().Log("verify empty request")
|
||||
balns, res, err = execFilterPaginate(store, nil, appCodec)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(res)
|
||||
s.Require().Equal(10, len(balns))
|
||||
s.Require().Equal(uint64(10), res.Total)
|
||||
s.Require().Nil(res.NextKey)
|
||||
|
||||
s.T().Log("verify default limit")
|
||||
pageReq = &query.PageRequest{Reverse: true}
|
||||
balns, res, err = execFilterPaginate(store, pageReq, appCodec)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(res)
|
||||
s.Require().Equal(10, len(balns))
|
||||
s.Require().Equal(uint64(10), res.Total)
|
||||
|
||||
s.T().Log("verify nextKey is returned if there are more results")
|
||||
pageReq = &query.PageRequest{Limit: 2, CountTotal: true, Reverse: true}
|
||||
balns, res, err = execFilterPaginate(store, pageReq, appCodec)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(res)
|
||||
s.Require().Equal(2, len(balns))
|
||||
s.Require().NotNil(res.NextKey)
|
||||
s.Require().Equal(string(res.NextKey), fmt.Sprintf("test7denom"))
|
||||
s.Require().Equal(uint64(10), res.Total)
|
||||
|
||||
s.T().Log("verify both key and offset can't be given")
|
||||
pageReq = &query.PageRequest{Key: res.NextKey, Limit: 1, Offset: 2, Reverse: true}
|
||||
_, _, err = execFilterPaginate(store, pageReq, appCodec)
|
||||
s.Require().Error(err)
|
||||
|
||||
s.T().Log("use nextKey for query and reverse true")
|
||||
pageReq = &query.PageRequest{Key: res.NextKey, Limit: 2, Reverse: true}
|
||||
balns, res, err = execFilterPaginate(store, pageReq, appCodec)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(res)
|
||||
s.Require().Equal(2, len(balns))
|
||||
s.Require().NotNil(res.NextKey)
|
||||
s.Require().Equal(string(res.NextKey), fmt.Sprintf("test5denom"))
|
||||
|
||||
s.T().Log("verify last page records, nextKey for query and reverse true")
|
||||
pageReq = &query.PageRequest{Key: res.NextKey, Reverse: true}
|
||||
balns, res, err = execFilterPaginate(store, pageReq, appCodec)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(res)
|
||||
s.Require().Equal(6, len(balns))
|
||||
s.Require().Nil(res.NextKey)
|
||||
|
||||
s.T().Log("verify Reverse pagination returns valid result")
|
||||
s.Require().Equal(balances[235:241].String(), balns.Sort().String())
|
||||
|
||||
}
|
||||
|
||||
func ExampleFilteredPaginate() {
|
||||
app, ctx, appCodec := setupTest()
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/store/types"
|
||||
db "github.com/tendermint/tm-db"
|
||||
)
|
||||
|
||||
// DefaultLimit is the default `limit` for queries
|
||||
@ -54,6 +55,7 @@ func Paginate(
|
||||
key := pageRequest.Key
|
||||
limit := pageRequest.Limit
|
||||
countTotal := pageRequest.CountTotal
|
||||
reverse := pageRequest.Reverse
|
||||
|
||||
if offset > 0 && key != nil {
|
||||
return nil, fmt.Errorf("invalid request, either offset or key is expected, got both")
|
||||
@ -67,13 +69,14 @@ func Paginate(
|
||||
}
|
||||
|
||||
if len(key) != 0 {
|
||||
iterator := prefixStore.Iterator(key, nil)
|
||||
iterator := getIterator(prefixStore, key, reverse)
|
||||
defer iterator.Close()
|
||||
|
||||
var count uint64
|
||||
var nextKey []byte
|
||||
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
|
||||
if count == limit {
|
||||
nextKey = iterator.Key()
|
||||
break
|
||||
@ -94,7 +97,7 @@ func Paginate(
|
||||
}, nil
|
||||
}
|
||||
|
||||
iterator := prefixStore.Iterator(nil, nil)
|
||||
iterator := getIterator(prefixStore, nil, reverse)
|
||||
defer iterator.Close()
|
||||
|
||||
end := offset + limit
|
||||
@ -132,3 +135,19 @@ func Paginate(
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func getIterator(prefixStore types.KVStore, start []byte, reverse bool) db.Iterator {
|
||||
if reverse {
|
||||
var end []byte
|
||||
if start != nil {
|
||||
itr := prefixStore.Iterator(start, nil)
|
||||
defer itr.Close()
|
||||
if itr.Valid() {
|
||||
itr.Next()
|
||||
end = itr.Key()
|
||||
}
|
||||
}
|
||||
return prefixStore.ReverseIterator(nil, end)
|
||||
}
|
||||
return prefixStore.Iterator(start, nil)
|
||||
}
|
||||
|
||||
@ -46,6 +46,8 @@ type PageRequest struct {
|
||||
// count_total is only respected when offset is used. It is ignored when key
|
||||
// is set.
|
||||
CountTotal bool `protobuf:"varint,4,opt,name=count_total,json=countTotal,proto3" json:"count_total,omitempty"`
|
||||
// reverse is set to true indicates that, results to be returned in the descending order.
|
||||
Reverse bool `protobuf:"varint,5,opt,name=reverse,proto3" json:"reverse,omitempty"`
|
||||
}
|
||||
|
||||
func (m *PageRequest) Reset() { *m = PageRequest{} }
|
||||
@ -109,6 +111,13 @@ func (m *PageRequest) GetCountTotal() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *PageRequest) GetReverse() bool {
|
||||
if m != nil {
|
||||
return m.Reverse
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// PageResponse is to be embedded in gRPC response messages where the
|
||||
// corresponding request message has used PageRequest.
|
||||
//
|
||||
@ -182,24 +191,25 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_53d6d609fe6828af = []byte{
|
||||
// 266 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x90, 0xc1, 0x4a, 0xc3, 0x40,
|
||||
0x14, 0x45, 0x33, 0xb6, 0xd6, 0x32, 0xed, 0x42, 0x06, 0x91, 0x74, 0x33, 0x86, 0xae, 0x82, 0x60,
|
||||
0x86, 0xe2, 0x07, 0x08, 0xdd, 0xba, 0x91, 0xe0, 0xca, 0x4d, 0x99, 0xc4, 0xd7, 0x18, 0xda, 0xcc,
|
||||
0x4b, 0x3b, 0x2f, 0x62, 0xfe, 0xc2, 0xcf, 0x72, 0xd9, 0xa5, 0x4b, 0x49, 0x7e, 0x44, 0x92, 0x09,
|
||||
0x74, 0x35, 0x73, 0x2f, 0x87, 0x77, 0xe0, 0xf2, 0xfb, 0x14, 0x6d, 0x81, 0x56, 0x25, 0xda, 0x82,
|
||||
0x3a, 0x54, 0x70, 0xac, 0xd5, 0xe7, 0x2a, 0x01, 0xd2, 0x2b, 0x55, 0xea, 0x2c, 0x37, 0x9a, 0x72,
|
||||
0x34, 0x51, 0x79, 0x44, 0x42, 0xb1, 0x70, 0x6c, 0xd4, 0xb1, 0x51, 0xcf, 0x46, 0x03, 0xbb, 0x34,
|
||||
0x7c, 0xf6, 0xa2, 0x33, 0x88, 0xe1, 0x50, 0x81, 0x25, 0x71, 0xcd, 0x47, 0x3b, 0xa8, 0x7d, 0x16,
|
||||
0xb0, 0x70, 0x1e, 0x77, 0x5f, 0x71, 0xcb, 0x27, 0xb8, 0xdd, 0x5a, 0x20, 0xff, 0x22, 0x60, 0xe1,
|
||||
0x38, 0x1e, 0x92, 0xb8, 0xe1, 0x97, 0xfb, 0xbc, 0xc8, 0xc9, 0x1f, 0xf5, 0xb5, 0x0b, 0xe2, 0x8e,
|
||||
0xcf, 0x52, 0xac, 0x0c, 0x6d, 0x08, 0x49, 0xef, 0xfd, 0x71, 0xc0, 0xc2, 0x69, 0xcc, 0xfb, 0xea,
|
||||
0xb5, 0x6b, 0x96, 0x4f, 0x7c, 0xee, 0x7c, 0xb6, 0x44, 0x63, 0x41, 0x2c, 0xf8, 0xd4, 0xc0, 0x17,
|
||||
0x6d, 0xce, 0xd6, 0xab, 0x2e, 0x3f, 0x43, 0xdd, 0x19, 0xdc, 0x15, 0x27, 0x76, 0x61, 0xbd, 0xfe,
|
||||
0x69, 0x24, 0x3b, 0x35, 0x92, 0xfd, 0x35, 0x92, 0x7d, 0xb7, 0xd2, 0x3b, 0xb5, 0xd2, 0xfb, 0x6d,
|
||||
0xa5, 0xf7, 0x16, 0x66, 0x39, 0x7d, 0x54, 0x49, 0x94, 0x62, 0xa1, 0x86, 0x71, 0xdc, 0xf3, 0x60,
|
||||
0xdf, 0x77, 0x8a, 0xea, 0x12, 0xac, 0x1b, 0x2a, 0x99, 0xf4, 0xb3, 0x3c, 0xfe, 0x07, 0x00, 0x00,
|
||||
0xff, 0xff, 0xb5, 0x65, 0x82, 0x18, 0x44, 0x01, 0x00, 0x00,
|
||||
// 282 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x90, 0xbd, 0x4e, 0xc3, 0x30,
|
||||
0x14, 0x85, 0x6b, 0xfa, 0x2b, 0xb7, 0x03, 0xb2, 0x10, 0x72, 0x17, 0x13, 0x75, 0x8a, 0x90, 0x88,
|
||||
0x55, 0xf1, 0x00, 0x48, 0x5d, 0x59, 0x50, 0xc4, 0xc4, 0x52, 0x39, 0xe1, 0x36, 0x44, 0x6d, 0xe2,
|
||||
0x34, 0xbe, 0xa9, 0xc8, 0x1b, 0x30, 0xf2, 0x58, 0x8c, 0x1d, 0x19, 0x51, 0xf2, 0x22, 0x28, 0x76,
|
||||
0x10, 0x93, 0xfd, 0x9d, 0x7b, 0x74, 0xef, 0xd1, 0xa1, 0xb7, 0xb1, 0x36, 0x99, 0x36, 0x32, 0x52,
|
||||
0x06, 0xe4, 0xb1, 0x82, 0xb2, 0x96, 0xa7, 0x75, 0x04, 0xa8, 0xd6, 0xb2, 0x50, 0x49, 0x9a, 0x2b,
|
||||
0x4c, 0x75, 0x1e, 0x14, 0xa5, 0x46, 0xcd, 0x96, 0xce, 0x1b, 0x74, 0xde, 0xc0, 0x7a, 0x83, 0xde,
|
||||
0xbb, 0xfa, 0x20, 0x74, 0xfe, 0xa4, 0x12, 0x08, 0xe1, 0x58, 0x81, 0x41, 0x76, 0x49, 0x87, 0x7b,
|
||||
0xa8, 0x39, 0xf1, 0x88, 0xbf, 0x08, 0xbb, 0x2f, 0xbb, 0xa6, 0x13, 0xbd, 0xdb, 0x19, 0x40, 0x7e,
|
||||
0xe1, 0x11, 0x7f, 0x14, 0xf6, 0xc4, 0xae, 0xe8, 0xf8, 0x90, 0x66, 0x29, 0xf2, 0xa1, 0x95, 0x1d,
|
||||
0xb0, 0x1b, 0x3a, 0x8f, 0x75, 0x95, 0xe3, 0x16, 0x35, 0xaa, 0x03, 0x1f, 0x79, 0xc4, 0x9f, 0x85,
|
||||
0xd4, 0x4a, 0xcf, 0x9d, 0xc2, 0x38, 0x9d, 0x96, 0x70, 0x82, 0xd2, 0x00, 0x1f, 0xdb, 0xe1, 0x1f,
|
||||
0xae, 0x1e, 0xe8, 0xc2, 0x25, 0x31, 0x85, 0xce, 0x0d, 0xb0, 0x25, 0x9d, 0xe5, 0xf0, 0x8e, 0xdb,
|
||||
0xff, 0x3c, 0xd3, 0x8e, 0x1f, 0xa1, 0xee, 0x6e, 0xbb, 0xfd, 0x2e, 0x92, 0x83, 0xcd, 0xe6, 0xab,
|
||||
0x11, 0xe4, 0xdc, 0x08, 0xf2, 0xd3, 0x08, 0xf2, 0xd9, 0x8a, 0xc1, 0xb9, 0x15, 0x83, 0xef, 0x56,
|
||||
0x0c, 0x5e, 0xfc, 0x24, 0xc5, 0xb7, 0x2a, 0x0a, 0x62, 0x9d, 0xc9, 0xbe, 0x37, 0xf7, 0xdc, 0x99,
|
||||
0xd7, 0xbd, 0xc4, 0xba, 0x00, 0xe3, 0x3a, 0x8c, 0x26, 0xb6, 0xb1, 0xfb, 0xdf, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0x3d, 0x43, 0x85, 0xf7, 0x5f, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *PageRequest) Marshal() (dAtA []byte, err error) {
|
||||
@ -222,6 +232,16 @@ func (m *PageRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.Reverse {
|
||||
i--
|
||||
if m.Reverse {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x28
|
||||
}
|
||||
if m.CountTotal {
|
||||
i--
|
||||
if m.CountTotal {
|
||||
@ -317,6 +337,9 @@ func (m *PageRequest) Size() (n int) {
|
||||
if m.CountTotal {
|
||||
n += 2
|
||||
}
|
||||
if m.Reverse {
|
||||
n += 2
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@ -463,6 +486,26 @@ func (m *PageRequest) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
}
|
||||
m.CountTotal = bool(v != 0)
|
||||
case 5:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Reverse", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowPagination
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.Reverse = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipPagination(dAtA[iNdEx:])
|
||||
|
||||
@ -169,6 +169,130 @@ func (s *paginationTestSuite) TestPagination() {
|
||||
s.Require().Nil(res.Pagination.NextKey)
|
||||
}
|
||||
|
||||
func (s *paginationTestSuite) TestReversePagination() {
|
||||
app, ctx, _ := setupTest()
|
||||
queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry())
|
||||
types.RegisterQueryServer(queryHelper, app.BankKeeper)
|
||||
queryClient := types.NewQueryClient(queryHelper)
|
||||
|
||||
var balances sdk.Coins
|
||||
|
||||
for i := 0; i < numBalances; i++ {
|
||||
denom := fmt.Sprintf("foo%ddenom", i)
|
||||
balances = append(balances, sdk.NewInt64Coin(denom, 100))
|
||||
}
|
||||
|
||||
balances = balances.Sort()
|
||||
addr1 := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address())
|
||||
acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1)
|
||||
app.AccountKeeper.SetAccount(ctx, acc1)
|
||||
s.Require().NoError(simapp.FundAccount(app, ctx, addr1, balances))
|
||||
|
||||
s.T().Log("verify paginate with custom limit and countTotal, Reverse false")
|
||||
pageReq := &query.PageRequest{Limit: 2, CountTotal: true, Reverse: true, Key: nil}
|
||||
request := types.NewQueryAllBalancesRequest(addr1, pageReq)
|
||||
res1, err := queryClient.AllBalances(gocontext.Background(), request)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res1.Balances.Len(), 2)
|
||||
s.Require().NotNil(res1.Pagination.NextKey)
|
||||
|
||||
s.T().Log("verify paginate with custom limit and countTotal, Reverse false")
|
||||
pageReq = &query.PageRequest{Limit: 150}
|
||||
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
|
||||
res1, err = queryClient.AllBalances(gocontext.Background(), request)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res1.Balances.Len(), 150)
|
||||
s.Require().NotNil(res1.Pagination.NextKey)
|
||||
s.Require().Equal(res1.Pagination.Total, uint64(0))
|
||||
|
||||
s.T().Log("verify paginate with custom limit, key and Reverse true")
|
||||
pageReq = &query.PageRequest{Limit: defaultLimit, Reverse: true}
|
||||
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
|
||||
res, err := queryClient.AllBalances(gocontext.Background(), request)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res.Balances.Len(), defaultLimit)
|
||||
s.Require().NotNil(res.Pagination.NextKey)
|
||||
s.Require().Equal(res.Pagination.Total, uint64(0))
|
||||
|
||||
s.T().Log("verify paginate with custom limit, key and Reverse true")
|
||||
pageReq = &query.PageRequest{Offset: 100, Limit: defaultLimit, Reverse: true}
|
||||
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
|
||||
res, err = queryClient.AllBalances(gocontext.Background(), request)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res.Balances.Len(), defaultLimit)
|
||||
s.Require().NotNil(res.Pagination.NextKey)
|
||||
s.Require().Equal(res.Pagination.Total, uint64(0))
|
||||
|
||||
s.T().Log("verify paginate for last page, Reverse true")
|
||||
pageReq = &query.PageRequest{Offset: 200, Limit: defaultLimit, Reverse: true}
|
||||
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
|
||||
res, err = queryClient.AllBalances(gocontext.Background(), request)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res.Balances.Len(), lastPageRecords)
|
||||
s.Require().Nil(res.Pagination.NextKey)
|
||||
s.Require().Equal(res.Pagination.Total, uint64(0))
|
||||
|
||||
s.T().Log("verify page request with limit > defaultLimit, returns less or equal to `limit` records")
|
||||
pageReq = &query.PageRequest{Limit: overLimit, Reverse: true}
|
||||
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
|
||||
res, err = queryClient.AllBalances(gocontext.Background(), request)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res.Pagination.Total, uint64(0))
|
||||
s.Require().NotNil(res.Pagination.NextKey)
|
||||
s.Require().LessOrEqual(res.Balances.Len(), overLimit)
|
||||
|
||||
s.T().Log("verify paginate with custom limit, key, countTotal false and Reverse true")
|
||||
pageReq = &query.PageRequest{Key: res1.Pagination.NextKey, Limit: 50, Reverse: true}
|
||||
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
|
||||
res, err = queryClient.AllBalances(gocontext.Background(), request)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res.Balances.Len(), 50)
|
||||
s.Require().NotNil(res.Pagination.NextKey)
|
||||
s.Require().Equal(res.Pagination.Total, uint64(0))
|
||||
|
||||
s.T().Log("verify Reverse pagination returns valid result")
|
||||
s.Require().Equal(balances[101:151].String(), res.Balances.Sort().String())
|
||||
|
||||
s.T().Log("verify paginate with custom limit, key, countTotal false and Reverse true")
|
||||
pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: 50, Reverse: true}
|
||||
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
|
||||
res, err = queryClient.AllBalances(gocontext.Background(), request)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res.Balances.Len(), 50)
|
||||
s.Require().NotNil(res.Pagination.NextKey)
|
||||
s.Require().Equal(res.Pagination.Total, uint64(0))
|
||||
|
||||
s.T().Log("verify Reverse pagination returns valid result")
|
||||
s.Require().Equal(balances[51:101].String(), res.Balances.Sort().String())
|
||||
|
||||
s.T().Log("verify paginate for last page Reverse true")
|
||||
pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: defaultLimit, Reverse: true}
|
||||
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
|
||||
res, err = queryClient.AllBalances(gocontext.Background(), request)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(res.Balances.Len(), 51)
|
||||
s.Require().Nil(res.Pagination.NextKey)
|
||||
s.Require().Equal(res.Pagination.Total, uint64(0))
|
||||
|
||||
s.T().Log("verify Reverse pagination returns valid result")
|
||||
s.Require().Equal(balances[0:51].String(), res.Balances.Sort().String())
|
||||
|
||||
s.T().Log("verify paginate with offset and key - error")
|
||||
pageReq = &query.PageRequest{Key: res1.Pagination.NextKey, Offset: 100, Limit: defaultLimit, CountTotal: false}
|
||||
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
|
||||
res, err = queryClient.AllBalances(gocontext.Background(), request)
|
||||
s.Require().Error(err)
|
||||
s.Require().Equal("rpc error: code = InvalidArgument desc = paginate: invalid request, either offset or key is expected, got both", err.Error())
|
||||
|
||||
s.T().Log("verify paginate with offset greater than total results")
|
||||
pageReq = &query.PageRequest{Offset: 300, Limit: defaultLimit, CountTotal: false, Reverse: true}
|
||||
request = types.NewQueryAllBalancesRequest(addr1, pageReq)
|
||||
res, err = queryClient.AllBalances(gocontext.Background(), request)
|
||||
s.Require().NoError(err)
|
||||
s.Require().LessOrEqual(res.Balances.Len(), 0)
|
||||
s.Require().Nil(res.Pagination.NextKey)
|
||||
}
|
||||
|
||||
func ExamplePaginate() {
|
||||
app, ctx, _ := setupTest()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user