forked from cerc-io/laconicd
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:
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 ®istrytypes.QueryRecordsResponse{Records: records}, nil
|
||||
return ®istrytypes.QueryRecordsResponse{Records: records, Pagination: pageResp}, nil
|
||||
}
|
||||
|
||||
func (qs queryServer) GetRecord(c context.Context, req *registrytypes.QueryGetRecordRequest) (*registrytypes.QueryGetRecordResponse, error) {
|
||||
|
||||
Reference in New Issue
Block a user