diff --git a/api/api_types.go b/api/api_types.go index c13d7504d..216fdaa6d 100644 --- a/api/api_types.go +++ b/api/api_types.go @@ -249,6 +249,7 @@ type TransactionReference struct { Index uint32 `json:"transactionIndex"` } +// TODO: remove this struct, indexertypes.Transaction is the same but includes blockHeight which is great type TransactionMetadata struct { Type string `json:"transactionType"` Number uint32 `json:"transactionNumber"` diff --git a/api/chain.go b/api/chain.go index 542cf22e8..8547c46c4 100644 --- a/api/chain.go +++ b/api/chain.go @@ -119,7 +119,7 @@ func (a *API) enableChainHandlers() error { "/chain/blocks/{height}/transactions/page/{page}", "GET", apirest.MethodAccessTypePublic, - a.chainTxByHeightHandler, + a.chainTxListByHeightAndPageHandler, ); err != nil { return err } @@ -143,7 +143,7 @@ func (a *API) enableChainHandlers() error { "/chain/transactions/page/{page}", "GET", apirest.MethodAccessTypePublic, - a.chainTxListPaginated, + a.chainTxListByPageHandler, ); err != nil { return err } @@ -317,7 +317,6 @@ func (a *API) organizationListByFilterAndPageHandler(msg *apirest.APIdata, ctx * // and sends it marshalled over ctx.Send // // Errors returned are always of type APIerror. -// func (a *API) sendOrganizationList(ctx *httprouter.HTTPContext, paramPage, paramLimit, paramOrgID string) error { func (a *API) sendOrganizationList(ctx *httprouter.HTTPContext, params *OrganizationParams) error { orgs, total, err := a.indexer.EntityList( params.Page*params.Limit, @@ -733,28 +732,31 @@ func (a *API) chainTxListByPageHandler(_ *apirest.APIdata, ctx *httprouter.HTTPC // @Param page path number true "Page" // @Success 200 {object} []TransactionMetadata // @Router /chain/blocks/{height}/transactions/page/{page} [get] -func (a *API) chainTxByHeightHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error { - height, err := strconv.ParseUint(ctx.URLParam("height"), 10, 64) +func (a *API) chainTxListByHeightAndPageHandler(_ *apirest.APIdata, ctx *httprouter.HTTPContext) error { + params, err := parseTransactionParams( + ctx.URLParam(ParamPage), + "", + ctx.URLParam(ParamHeight), + ) if err != nil { return err } - block := a.vocapp.GetBlockByHeight(int64(height)) + + // TODO: replace all of this with the indexer method, + // from the other PR, TransactionListByHeight + block := a.vocapp.GetBlockByHeight(int64(params.Height)) if block == nil { return ErrBlockNotFound } blockTxs := &BlockTransactionsInfo{ - BlockNumber: height, + BlockNumber: params.Height, TransactionsCount: uint32(len(block.Txs)), Transactions: make([]TransactionMetadata, 0), } - page, err := parsePage(ctx.URLParam(ParamPage)) - if err != nil { - return err - } count := 0 - for i := page * DefaultItemsPerPage; i < len(block.Txs); i++ { - if count >= DefaultItemsPerPage { + for i := params.Page * params.Limit; i < len(block.Txs); i++ { + if count >= params.Limit { break } signedTx := new(models.SignedTx) @@ -790,6 +792,38 @@ func (a *API) chainTxByHeightHandler(_ *apirest.APIdata, ctx *httprouter.HTTPCon return ctx.Send(data, apirest.HTTPstatusOK) } +// sendTransactionList produces a filtered, paginated TransactionList, +// and sends it marshalled over ctx.Send +// +// Errors returned are always of type APIerror. +func (a *API) sendTransactionList(ctx *httprouter.HTTPContext, params *TransactionParams) error { + txs, total, err := a.indexer.SearchTransactions( + int32(params.Limit), + int32(params.Page*params.Limit), + ) + if err != nil { + return ErrIndexerQueryFailed.WithErr(err) + } + + if params.Page == 0 && total == 0 { + return ErrTransactionNotFound + } + + pagination, err := calculatePagination(params.Page, params.Limit, total) + if err != nil { + return err + } + + list := &TransactionsList{ + Pagination: pagination, + } + for _, tx := range txs { + list.Transactions = append(list.Transactions, *tx) + } + + return marshalAndSend(ctx, list) +} + // chainValidatorsHandler // // @Summary List validators @@ -1112,3 +1146,21 @@ func parseFeesParams(paramPage, paramLimit, paramReference, paramType, paramAcco AccountID: util.TrimHex(paramAccountId), }, nil } + +// parseTransactionParams returns an TransactionParams filled with the passed params +func parseTransactionParams(paramPage, paramLimit, paramHeight string) (*TransactionParams, error) { + pagination, err := parsePaginationParams(paramPage, paramLimit) + if err != nil { + return nil, err + } + + height, err := parseNumber(paramHeight) + if err != nil { + return nil, err + } + + return &TransactionParams{ + PaginationParams: pagination, + Height: uint64(height), + }, nil +} diff --git a/vochain/indexer/transaction.go b/vochain/indexer/transaction.go index 5d8097886..7abd56cf3 100644 --- a/vochain/indexer/transaction.go +++ b/vochain/indexer/transaction.go @@ -61,6 +61,12 @@ func (idx *Indexer) GetTxHashReference(hash types.HexBytes) (*indexertypes.Trans return indexertypes.TransactionFromDB(&sqlTxRef), nil } +// SearchTransactions TBD, doesn't return the real totalItems +func (idx *Indexer) SearchTransactions(limit, offset int32) ([]*indexertypes.Transaction, uint64, error) { + list, err := idx.GetLastTransactions(limit, offset) + return list, uint64(len(list)), err +} + // GetLastTransactions fetches a number of the latest indexed transactions. // The first one returned is the newest, so they are in descending order. func (idx *Indexer) GetLastTransactions(limit, offset int32) ([]*indexertypes.Transaction, error) {