Merge PR #4658: Generalize Querier REST Pagination

This commit is contained in:
Alexander Bezobchuk 2019-07-02 09:00:46 -04:00 committed by GitHub
parent b3a8195e31
commit aba1f649ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 102 additions and 41 deletions

View File

@ -0,0 +1,2 @@
#4601 Implement generic pangination helper function to be used in
REST handlers and queriers.

30
client/utils.go Normal file
View File

@ -0,0 +1,30 @@
package client
// Paginate returns the correct starting and ending index for a paginated query,
// given that client provides a desired page and limit of objects and the handler
// provides the total number of objects. If the start page is invalid, non-positive
// values are returned signaling the request is invalid.
//
// NOTE: The start page is assumed to be 1-indexed.
func Paginate(numObjs, page, limit, defLimit int) (start, end int) {
if page == 0 {
// invalid start page
return -1, -1
} else if limit == 0 {
limit = defLimit
}
start = (page - 1) * limit
end = limit + start
if end >= numObjs {
end = numObjs
}
if start >= numObjs {
// page is out of bounds
return -1, -1
}
return start, end
}

61
client/utils_test.go Normal file
View File

@ -0,0 +1,61 @@
package client_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client"
)
func TestPaginate(t *testing.T) {
testCases := []struct {
name string
numObjs, page, limit, defLimit int
expectedStart, expectedEnd int
}{
{
"all objects in a single page",
100, 1, 100, 100,
0, 100,
},
{
"page one of three",
75, 1, 25, 100,
0, 25,
},
{
"page two of three",
75, 2, 25, 100,
25, 50,
},
{
"page three of three",
75, 3, 25, 100,
50, 75,
},
{
"end is greater than total number of objects",
75, 2, 50, 100,
50, 75,
},
{
"invalid start page",
75, 4, 25, 100,
-1, -1,
},
{
"invalid zero start page",
75, 0, 25, 100,
-1, -1,
},
}
for i, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
start, end := client.Paginate(tc.numObjs, tc.page, tc.limit, tc.defLimit)
require.Equal(t, tc.expectedStart, start, "invalid result; test case #%d", i)
require.Equal(t, tc.expectedEnd, end, "invalid result; test case #%d", i)
})
}
}

View File

@ -5,6 +5,7 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -65,11 +66,6 @@ func querySigningInfos(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
}
if params.Limit == 0 {
// set the default limit to max bonded if no limit was provided
params.Limit = int(k.sk.MaxValidators(ctx))
}
var signingInfos []ValidatorSigningInfo
k.IterateValidatorSigningInfos(ctx, func(consAddr sdk.ConsAddress, info ValidatorSigningInfo) (stop bool) {
@ -77,15 +73,8 @@ func querySigningInfos(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte
return false
})
// get pagination bounds
start := (params.Page - 1) * params.Limit
end := params.Limit + start
if end >= len(signingInfos) {
end = len(signingInfos)
}
if start >= len(signingInfos) {
// page is out of bounds
start, end := client.Paginate(len(signingInfos), params.Page, params.Limit, int(k.sk.MaxValidators(ctx)))
if start < 0 || end < 0 {
signingInfos = []ValidatorSigningInfo{}
} else {
signingInfos = signingInfos[start:end]

View File

@ -6,6 +6,7 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking/types"
@ -55,11 +56,6 @@ func queryValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte,
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
}
stakingParams := k.GetParams(ctx)
if params.Limit == 0 {
params.Limit = int(stakingParams.MaxValidators)
}
validators := k.GetAllValidators(ctx)
filteredVals := make([]types.Validator, 0, len(validators))
@ -69,15 +65,8 @@ func queryValidators(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte,
}
}
// get pagination bounds
start := (params.Page - 1) * params.Limit
end := params.Limit + start
if end >= len(filteredVals) {
end = len(filteredVals)
}
if start >= len(filteredVals) {
// page is out of bounds
start, end := client.Paginate(len(filteredVals), params.Page, params.Limit, int(k.GetParams(ctx).MaxValidators))
if start < 0 || end < 0 {
filteredVals = []types.Validator{}
} else {
filteredVals = filteredVals[start:end]

View File

@ -5,6 +5,7 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/supply/types"
)
@ -32,20 +33,9 @@ func queryTotalSupply(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte,
}
totalSupply := k.GetSupply(ctx).Total
totalSupplyLen := len(totalSupply)
if params.Limit == 0 {
params.Limit = totalSupplyLen
}
start := (params.Page - 1) * params.Limit
end := params.Limit + start
if end >= totalSupplyLen {
end = totalSupplyLen
}
if start >= totalSupplyLen {
// page is out of bounds
start, end := client.Paginate(len(totalSupply), params.Page, params.Limit, 100)
if start < 0 || end < 0 {
totalSupply = sdk.Coins{}
} else {
totalSupply = totalSupply[start:end]