Add pagination for query to get records (#58)

Part of [Create a public laconicd testnet](https://www.notion.so/Create-a-public-laconicd-testnet-896a11bdd8094eff8f1b49c0be0ca3b8)
Handles cerc-io/laconic-console#59

Co-authored-by: IshaVenikar <ishavenikar7@gmail.com>
Reviewed-on: cerc-io/laconicd#58
Co-authored-by: Prathamesh Musale <prathamesh.musale0@gmail.com>
Co-committed-by: Prathamesh Musale <prathamesh.musale0@gmail.com>
This commit is contained in:
2024-09-05 07:33:48 +00:00
committed by nabarun
co-authored by IshaVenikar
parent 94ba057807
commit df43322ef3
8 changed files with 216 additions and 112 deletions
+1 -1
View File
@@ -80,7 +80,7 @@ func (k Keeper) OnboardParticipant(
}
if !params.OnboardingEnabled {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Validator onboarding is disabled")
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "Onboarding is disabled")
}
message, err := json.Marshal(msg.EthPayload)
+1 -1
View File
@@ -62,7 +62,7 @@ func (k *Keeper) ExportGenesis(ctx sdk.Context) (*registry.GenesisState, error)
return nil, err
}
records, err := k.ListRecords(ctx)
records, _, err := k.PaginatedListRecords(ctx, nil)
if err != nil {
return nil, err
}
+103 -21
View File
@@ -16,6 +16,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec/legacy"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
auth "github.com/cosmos/cosmos-sdk/x/auth/keeper"
bank "github.com/cosmos/cosmos-sdk/x/bank/keeper"
"github.com/gibson042/canonicaljson-go"
@@ -204,23 +205,38 @@ func (k Keeper) HasRecord(ctx sdk.Context, id string) (bool, error) {
return has, nil
}
// ListRecords - get all records.
func (k Keeper) ListRecords(ctx sdk.Context) ([]registrytypes.Record, error) {
// PaginatedListRecords - get all records with optional pagination.
func (k Keeper) PaginatedListRecords(ctx sdk.Context, pagination *query.PageRequest) ([]registrytypes.Record, *query.PageResponse, error) {
var records []registrytypes.Record
var pageResp *query.PageResponse
err := k.Records.Walk(ctx, nil, func(key string, value registrytypes.Record) (bool, error) {
if err := k.populateRecordNames(ctx, &value); err != nil {
return true, err
if pagination == nil {
err := k.Records.Walk(ctx, nil, func(key string, value registrytypes.Record) (bool, error) {
if err := k.populateRecordNames(ctx, &value); err != nil {
return true, err
}
records = append(records, value)
return false, nil
})
if err != nil {
return nil, nil, err
}
records = append(records, value)
} else {
var err error
records, pageResp, err = query.CollectionPaginate(ctx, k.Records, pagination, func(key string, value registrytypes.Record) (registrytypes.Record, error) {
if err := k.populateRecordNames(ctx, &value); err != nil {
return registrytypes.Record{}, err
}
return false, nil
})
if err != nil {
return nil, err
return value, nil
})
if err != nil {
return nil, nil, err
}
}
return records, nil
return records, pageResp, nil
}
// GetRecordById - gets a record from the store.
@@ -261,36 +277,47 @@ func (k Keeper) GetRecordsByBondId(ctx sdk.Context, bondId string) ([]registryty
return records, nil
}
// RecordsFromAttributes gets a list of records whose attributes match all provided values
func (k Keeper) RecordsFromAttributes(
// PaginatedRecordsFromAttributes gets a list of records whose attributes match all provided values
// with optional pagination.
func (k Keeper) PaginatedRecordsFromAttributes(
ctx sdk.Context,
attributes []*registrytypes.QueryRecordsRequest_KeyValueInput,
all bool,
) ([]registrytypes.Record, error) {
resultRecordIds := []string{}
pagination *query.PageRequest,
) ([]registrytypes.Record, *query.PageResponse, error) {
var resultRecordIds []string
var pageResp *query.PageResponse
filteredRecordIds := []string{}
for i, attr := range attributes {
suffix, err := QueryValueToJSON(attr.Value)
if err != nil {
return nil, err
return nil, nil, err
}
mapKey := collections.Join(attr.Key, string(suffix))
recordIds, err := k.getAttributeMapping(ctx, mapKey)
if err != nil {
return nil, err
return nil, nil, err
}
if i == 0 {
resultRecordIds = recordIds
filteredRecordIds = recordIds
} else {
resultRecordIds = getIntersection(recordIds, resultRecordIds)
filteredRecordIds = getIntersection(recordIds, filteredRecordIds)
}
}
if pagination != nil {
resultRecordIds, pageResp = paginate(filteredRecordIds, pagination)
} else {
resultRecordIds = filteredRecordIds
}
records := []registrytypes.Record{}
for _, id := range resultRecordIds {
record, err := k.GetRecordById(ctx, id)
if err != nil {
return nil, err
return nil, nil, err
}
if record.Deleted {
continue
@@ -301,7 +328,7 @@ func (k Keeper) RecordsFromAttributes(
records = append(records, record)
}
return records, nil
return records, pageResp, nil
}
// TODO not recursive, and only should be if we want to support querying with whole sub-objects,
@@ -717,6 +744,38 @@ func (k Keeper) tryTakeRecordRent(ctx sdk.Context, record registrytypes.Record)
return k.SaveRecord(ctx, record)
}
// paginate implements basic pagination over a list of objects
func paginate[T any](data []T, pagination *query.PageRequest) ([]T, *query.PageResponse) {
pageReq := initPageRequestDefaults(pagination)
offset := pageReq.Offset
limit := pageReq.Limit
countTotal := pageReq.CountTotal
totalItems := uint64(len(data))
start := offset
end := offset + limit
if start > totalItems {
if countTotal {
return []T{}, &query.PageResponse{Total: 0}
} else {
return []T{}, nil
}
}
if end > totalItems {
end = totalItems
}
paginatedItems := data[start:end]
if countTotal {
return paginatedItems, &query.PageResponse{Total: totalItems}
} else {
return paginatedItems, nil
}
}
func getIntersection(a []string, b []string) []string {
result := []string{}
if len(a) < len(b) {
@@ -743,3 +802,26 @@ func contains(arr []string, str string) bool {
}
return false
}
// https://github.com/cosmos/cosmos-sdk/blob/v0.50.3/types/query/pagination.go#L141
// initPageRequestDefaults initializes a PageRequest's defaults when those are not set.
func initPageRequestDefaults(pageRequest *query.PageRequest) *query.PageRequest {
// if the PageRequest is nil, use default PageRequest
if pageRequest == nil {
pageRequest = &query.PageRequest{}
}
pageRequestCopy := *pageRequest
if len(pageRequestCopy.Key) == 0 {
pageRequestCopy.Key = nil
}
if pageRequestCopy.Limit == 0 {
pageRequestCopy.Limit = query.DefaultLimit
// count total results when the limit is zero/not supplied
pageRequestCopy.CountTotal = true
}
return &pageRequestCopy
}
+5 -3
View File
@@ -6,6 +6,7 @@ import (
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
registrytypes "git.vdb.to/cerc-io/laconicd/x/registry"
)
@@ -39,20 +40,21 @@ func (qs queryServer) Records(c context.Context, req *registrytypes.QueryRecords
all := req.GetAll()
var records []registrytypes.Record
var pageResp *query.PageResponse
var err error
if len(attributes) > 0 {
records, err = qs.k.RecordsFromAttributes(ctx, attributes, all)
records, pageResp, err = qs.k.PaginatedRecordsFromAttributes(ctx, attributes, all, req.Pagination)
if err != nil {
return nil, err
}
} else {
records, err = qs.k.ListRecords(ctx)
records, pageResp, err = qs.k.PaginatedListRecords(ctx, req.Pagination)
if err != nil {
return nil, err
}
}
return &registrytypes.QueryRecordsResponse{Records: records}, nil
return &registrytypes.QueryRecordsResponse{Records: records, Pagination: pageResp}, nil
}
func (qs queryServer) GetRecord(c context.Context, req *registrytypes.QueryGetRecordRequest) (*registrytypes.QueryGetRecordResponse, error) {