Skip to content

Commit

Permalink
Support query txs' TotalCount in GET /txs (#4214)
Browse files Browse the repository at this point in the history
Closes: #3942
  • Loading branch information
Frank Yang authored and Alessio Treglia committed May 4, 2019
1 parent 67ab0b1 commit 1cfc868
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 78 deletions.
1 change: 1 addition & 0 deletions .pending/breaking/gaiarest/3942-Support-query-t
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#3942 Update pagination data in txs query.
32 changes: 27 additions & 5 deletions client/lcd/swagger-ui/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,7 @@ paths:
200:
description: All txs matching the provided tags
schema:
type: array
items:
$ref: "#/definitions/TxQuery"
$ref: "#/definitions/PaginatedQueryTxs"
400:
description: Invalid search tags
500:
Expand Down Expand Up @@ -1963,8 +1961,10 @@ definitions:
properties:
hash:
type: string
example: "D085138D913993919295FF4B0A9107F1F2CDE0D37A87CE0644E217CBF3B49656"
height:
type: number
example: 368
tx:
$ref: "#/definitions/StdTx"
result:
Expand All @@ -1974,14 +1974,36 @@ definitions:
type: string
gas_wanted:
type: string
example: "0"
example: "200000"
gas_used:
type: string
example: "0"
example: "26354"
tags:
type: array
items:
$ref: "#/definitions/KVPair"
PaginatedQueryTxs:
type: object
properties:
total_count:
type: number
example: 1
count:
type: number
example: 1
page_number:
type: number
example: 1
page_total:
type: number
example: 1
limit:
type: number
example: 30
txs:
type: array
items:
$ref: "#/definitions/TxQuery"
StdTx:
type: object
properties:
Expand Down
4 changes: 2 additions & 2 deletions client/tx/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,13 @@ func QueryTxsByTagsRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec)
return
}

txs, err = SearchTxs(cliCtx, cdc, tags, page, limit)
searchResult, err := SearchTxs(cliCtx, cdc, tags, page, limit)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent)
rest.PostProcessResponse(w, cdc, searchResult, cliCtx.Indent)
}
}

Expand Down
6 changes: 4 additions & 2 deletions client/tx/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
// SearchTxs performs a search for transactions for a given set of tags via
// Tendermint RPC. It returns a slice of Info object containing txs and metadata.
// An error is returned if the query fails.
func SearchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string, page, limit int) ([]sdk.TxResponse, error) {
func SearchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string, page, limit int) (*sdk.SearchTxsResult, error) {
if len(tags) == 0 {
return nil, errors.New("must declare at least one tag to search")
}
Expand Down Expand Up @@ -64,7 +64,9 @@ func SearchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string, page,
return nil, err
}

return txs, nil
result := sdk.NewSearchTxsResult(resTxs.TotalCount, len(txs), page, limit, txs)

return &result, nil
}

// formatTxResults parses the indexed txs into a slice of TxResponse objects.
Expand Down
37 changes: 19 additions & 18 deletions cmd/gaia/cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,8 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
tests.WaitForNextNBlocksTM(1, f.Port)

// Ensure transaction tags can be queried
txs := f.QueryTxs(1, 50, "action:submit_proposal", fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, txs, 1)
searchResult := f.QueryTxs(1, 50, "action:submit_proposal", fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, searchResult.Txs, 1)

// Ensure deposit was deducted
fooAcc = f.QueryAccount(fooAddr)
Expand Down Expand Up @@ -555,8 +555,8 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, proposalTokens.Add(depositTokens), deposit.Amount.AmountOf(denom))

// Ensure tags are set on the transaction
txs = f.QueryTxs(1, 50, "action:deposit", fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, txs, 1)
searchResult = f.QueryTxs(1, 50, "action:deposit", fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, searchResult.Txs, 1)

// Ensure account has expected amount of funds
fooAcc = f.QueryAccount(fooAddr)
Expand Down Expand Up @@ -592,8 +592,8 @@ func TestGaiaCLISubmitProposal(t *testing.T) {
require.Equal(t, gov.OptionYes, votes[0].Option)

// Ensure tags are applied to voting transaction properly
txs = f.QueryTxs(1, 50, "action:vote", fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, txs, 1)
searchResult = f.QueryTxs(1, 50, "action:vote", fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, searchResult.Txs, 1)

// Ensure no proposals in deposit period
proposalsQuery = f.QueryGovProposals("--status=DepositPeriod")
Expand Down Expand Up @@ -654,8 +654,8 @@ func TestGaiaCLISubmitParamChangeProposal(t *testing.T) {
tests.WaitForNextNBlocksTM(1, f.Port)

// ensure transaction tags can be queried
txs := f.QueryTxs(1, 50, "action:submit_proposal", fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, txs, 1)
txsPage := f.QueryTxs(1, 50, "action:submit_proposal", fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, txsPage.Txs, 1)

// ensure deposit was deducted
fooAcc = f.QueryAccount(fooAddr)
Expand Down Expand Up @@ -700,25 +700,26 @@ func TestGaiaCLIQueryTxPagination(t *testing.T) {

// perPage = 15, 2 pages
txsPage1 := f.QueryTxs(1, 15, fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, txsPage1, 15)
require.Len(t, txsPage1.Txs, 15)
require.Equal(t, txsPage1.Count, 15)
txsPage2 := f.QueryTxs(2, 15, fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, txsPage2, 15)
require.NotEqual(t, txsPage1, txsPage2)
require.Len(t, txsPage2.Txs, 15)
require.NotEqual(t, txsPage1.Txs, txsPage2.Txs)
txsPage3 := f.QueryTxs(3, 15, fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, txsPage3, 15)
require.Equal(t, txsPage2, txsPage3)
require.Len(t, txsPage3.Txs, 15)
require.Equal(t, txsPage2.Txs, txsPage3.Txs)

// perPage = 16, 2 pages
txsPage1 = f.QueryTxs(1, 16, fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, txsPage1, 16)
require.Len(t, txsPage1.Txs, 16)
txsPage2 = f.QueryTxs(2, 16, fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, txsPage2, 14)
require.NotEqual(t, txsPage1, txsPage2)
require.Len(t, txsPage2.Txs, 14)
require.NotEqual(t, txsPage1.Txs, txsPage2.Txs)

// perPage = 50
txsPageFull := f.QueryTxs(1, 50, fmt.Sprintf("sender:%s", fooAddr))
require.Len(t, txsPageFull, 30)
require.Equal(t, txsPageFull, append(txsPage1, txsPage2...))
require.Len(t, txsPageFull.Txs, 30)
require.Equal(t, txsPageFull.Txs, append(txsPage1.Txs, txsPage2.Txs...))

// perPage = 0
f.QueryTxsInvalid(errors.New("ERROR: page must greater than 0"), 0, 50, fmt.Sprintf("sender:%s", fooAddr))
Expand Down
8 changes: 4 additions & 4 deletions cmd/gaia/cli_test/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,14 +425,14 @@ func (f *Fixtures) QueryAccount(address sdk.AccAddress, flags ...string) auth.Ba
// gaiacli query txs

// QueryTxs is gaiacli query txs
func (f *Fixtures) QueryTxs(page, limit int, tags ...string) []sdk.TxResponse {
func (f *Fixtures) QueryTxs(page, limit int, tags ...string) *sdk.SearchTxsResult {
cmd := fmt.Sprintf("%s query txs --page=%d --limit=%d --tags='%s' %v", f.GaiacliBinary, page, limit, queryTags(tags), f.Flags())
out, _ := tests.ExecuteT(f.T, cmd, "")
var txs []sdk.TxResponse
var result sdk.SearchTxsResult
cdc := app.MakeCodec()
err := cdc.UnmarshalJSON([]byte(out), &txs)
err := cdc.UnmarshalJSON([]byte(out), &result)
require.NoError(f.T, err, "out %v\n, err %v", out, err)
return txs
return &result
}

// QueryTxsInvalid query txs with wrong parameters and compare expected error
Expand Down
9 changes: 5 additions & 4 deletions cmd/gaia/lcd_test/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -545,18 +545,19 @@ func getTransactionRequest(t *testing.T, port, hash string) (*http.Response, str
// POST /txs broadcast txs

// GET /txs search transactions
func getTransactions(t *testing.T, port string, tags ...string) []sdk.TxResponse {
func getTransactions(t *testing.T, port string, tags ...string) *sdk.SearchTxsResult {
var txs []sdk.TxResponse
result := sdk.NewSearchTxsResult(0, 0, 1, 30, txs)
if len(tags) == 0 {
return txs
return &result
}
queryStr := strings.Join(tags, "&")
res, body := Request(t, port, "GET", fmt.Sprintf("/txs?%s", queryStr), nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)

err := cdc.UnmarshalJSON([]byte(body), &txs)
err := cdc.UnmarshalJSON([]byte(body), &result)
require.NoError(t, err)
return txs
return &result
}

// ----------------------------------------------------------------------
Expand Down
60 changes: 30 additions & 30 deletions cmd/gaia/lcd_test/lcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,19 +324,19 @@ func TestTxs(t *testing.T) {
defer cleanup()

var emptyTxs []sdk.TxResponse
txs := getTransactions(t, port)
require.Equal(t, emptyTxs, txs)
txResult := getTransactions(t, port)
require.Equal(t, emptyTxs, txResult.Txs)

// query empty
txs = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String()))
require.Equal(t, emptyTxs, txs)
txResult = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String()))
require.Equal(t, emptyTxs, txResult.Txs)

// also tests url decoding
txs = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String()))
require.Equal(t, emptyTxs, txs)
txResult = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String()))
require.Equal(t, emptyTxs, txResult.Txs)

txs = getTransactions(t, port, fmt.Sprintf("action=submit%%20proposal&sender=%s", addr.String()))
require.Equal(t, emptyTxs, txs)
txResult = getTransactions(t, port, fmt.Sprintf("action=submit%%20proposal&sender=%s", addr.String()))
require.Equal(t, emptyTxs, txResult.Txs)

// create tx
receiveAddr, resultTx := doTransfer(t, port, seed, name1, memo, pw, addr, fees)
Expand All @@ -347,14 +347,14 @@ func TestTxs(t *testing.T) {
require.Equal(t, resultTx.TxHash, tx.TxHash)

// query sender
txs = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String()))
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
txResult = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String()))
require.Len(t, txResult.Txs, 1)
require.Equal(t, resultTx.Height, txResult.Txs[0].Height)

// query recipient
txs = getTransactions(t, port, fmt.Sprintf("recipient=%s", receiveAddr.String()))
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
txResult = getTransactions(t, port, fmt.Sprintf("recipient=%s", receiveAddr.String()))
require.Len(t, txResult.Txs, 1)
require.Equal(t, resultTx.Height, txResult.Txs[0].Height)

// query transaction that doesn't exist
validTxHash := "9ADBECAAD8DACBEC3F4F535704E7CF715C765BDCEDBEF086AFEAD31BA664FB0B"
Expand Down Expand Up @@ -453,12 +453,12 @@ func TestBonding(t *testing.T) {
require.Equal(t, uint32(0), resultTx.Code)

// query tx
txs := getTransactions(t, port,
txResult := getTransactions(t, port,
fmt.Sprintf("action=delegate&sender=%s", addr),
fmt.Sprintf("destination-validator=%s", operAddrs[0]),
)
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
require.Len(t, txResult.Txs, 1)
require.Equal(t, resultTx.Height, txResult.Txs[0].Height)

// verify balance
acc = getAccount(t, port, addr)
Expand Down Expand Up @@ -506,12 +506,12 @@ func TestBonding(t *testing.T) {
expectedBalance = coins[0]

// query tx
txs = getTransactions(t, port,
txResult = getTransactions(t, port,
fmt.Sprintf("action=begin_unbonding&sender=%s", addr),
fmt.Sprintf("source-validator=%s", operAddrs[0]),
)
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
require.Len(t, txResult.Txs, 1)
require.Equal(t, resultTx.Height, txResult.Txs[0].Height)

ubd := getUnbondingDelegation(t, port, addr, operAddrs[0])
require.Len(t, ubd.Entries, 1)
Expand Down Expand Up @@ -543,13 +543,13 @@ func TestBonding(t *testing.T) {
)

// query tx
txs = getTransactions(t, port,
txResult = getTransactions(t, port,
fmt.Sprintf("action=begin_redelegate&sender=%s", addr),
fmt.Sprintf("source-validator=%s", operAddrs[0]),
fmt.Sprintf("destination-validator=%s", operAddrs[1]),
)
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
require.Len(t, txResult.Txs, 1)
require.Equal(t, resultTx.Height, txResult.Txs[0].Height)

redelegation := getRedelegations(t, port, addr, operAddrs[0], operAddrs[1])
require.Len(t, redelegation, 1)
Expand Down Expand Up @@ -577,7 +577,7 @@ func TestBonding(t *testing.T) {
// require.Equal(t, sdk.Unbonding, bondedValidators[0].Status)

// query txs
txs = getBondingTxs(t, port, addr, "")
txs := getBondingTxs(t, port, addr, "")
require.Len(t, txs, 3, "All Txs found")

txs = getBondingTxs(t, port, addr, "bond")
Expand Down Expand Up @@ -709,9 +709,9 @@ func TestDeposit(t *testing.T) {
require.Equal(t, expectedBalance.Amount.Sub(depositTokens), acc.GetCoins().AmountOf(sdk.DefaultBondDenom))

// query tx
txs := getTransactions(t, port, fmt.Sprintf("action=deposit&sender=%s", addr))
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
txResult := getTransactions(t, port, fmt.Sprintf("action=deposit&sender=%s", addr))
require.Len(t, txResult.Txs, 1)
require.Equal(t, resultTx.Height, txResult.Txs[0].Height)

// query proposal
totalCoins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, sdk.TokensFromTendermintPower(10))}
Expand Down Expand Up @@ -770,9 +770,9 @@ func TestVote(t *testing.T) {
expectedBalance = coins[0]

// query tx
txs := getTransactions(t, port, fmt.Sprintf("action=vote&sender=%s", addr))
require.Len(t, txs, 1)
require.Equal(t, resultTx.Height, txs[0].Height)
txResult := getTransactions(t, port, fmt.Sprintf("action=vote&sender=%s", addr))
require.Len(t, txResult.Txs, 1)
require.Equal(t, resultTx.Height, txResult.Txs[0].Height)

vote := getVote(t, port, proposalID, addr)
require.Equal(t, proposalID, vote.ProposalID)
Expand Down
22 changes: 22 additions & 0 deletions types/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"math"
"strings"

ctypes "github.com/tendermint/tendermint/rpc/core/types"
Expand Down Expand Up @@ -249,6 +250,27 @@ func (r TxResponse) Empty() bool {
return r.TxHash == "" && r.Logs == nil
}

// SearchTxsResult defines a structure for querying txs pageable
type SearchTxsResult struct {
TotalCount int `json:"total_count"` // Count of all txs
Count int `json:"count"` // Count of txs in current page
PageNumber int `json:"page_number"` // Index of current page, start from 1
PageTotal int `json:"page_total"` // Count of total pages
Limit int `json:"limit"` // Max count txs per page
Txs []TxResponse `json:"txs"` // List of txs in current page
}

func NewSearchTxsResult(totalCount, count, page, limit int, txs []TxResponse) SearchTxsResult {
return SearchTxsResult{
TotalCount: totalCount,
Count: count,
PageNumber: page,
PageTotal: int(math.Ceil(float64(totalCount) / float64(limit))),
Limit: limit,
Txs: txs,
}
}

// ParseABCILogs attempts to parse a stringified ABCI tx log into a slice of
// ABCIMessageLog types. It returns an error upon JSON decoding failure.
func ParseABCILogs(logs string) (res ABCIMessageLogs, err error) {
Expand Down
Loading

0 comments on commit 1cfc868

Please sign in to comment.