Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize Querier REST Pagination #4658

Merged
merged 6 commits into from
Jul 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .pending/improvements/sdk/4601-Implement-gener
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#4601 Implement generic pangination helper function to be used in
REST handlers and queriers.
30 changes: 30 additions & 0 deletions client/utils.go
Original file line number Diff line number Diff line change
@@ -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 changes: 61 additions & 0 deletions client/utils_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
}
17 changes: 3 additions & 14 deletions x/slashing/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -65,27 +66,15 @@ 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) {
signingInfos = append(signingInfos, info)
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]
Expand Down
17 changes: 3 additions & 14 deletions x/staking/keeper/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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))

Expand All @@ -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]
Expand Down
16 changes: 3 additions & 13 deletions x/supply/keeper/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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]
Expand Down