From d2e9196fbaa050002922d182146d94d8e71f079a Mon Sep 17 00:00:00 2001 From: Arnau B Date: Mon, 9 Nov 2020 12:56:35 +0100 Subject: [PATCH] Refactor api pagination --- api/account.go | 11 ++- api/account_test.go | 26 +++---- api/api_test.go | 64 ++++++++++------ api/batch.go | 11 ++- api/batch_test.go | 18 ++--- api/bids.go | 11 ++- api/bids_test.go | 18 ++--- api/coordinator.go | 7 +- api/coordinator_test.go | 16 ++-- api/exits.go | 11 ++- api/exits_test.go | 18 ++--- api/slots.go | 85 ++++++++------------- api/slots_test.go | 20 ++--- api/swagger.yml | 74 ++++++++---------- api/token.go | 11 ++- api/token_test.go | 18 ++--- api/txshistory.go | 11 ++- api/txshistory_test.go | 18 ++--- db/historydb/historydb.go | 153 ++++++++++++++++---------------------- db/utils.go | 15 ---- 20 files changed, 257 insertions(+), 359 deletions(-) diff --git a/api/account.go b/api/account.go index 35a455e..5bcaed1 100644 --- a/api/account.go +++ b/api/account.go @@ -5,7 +5,6 @@ import ( "github.com/gin-gonic/gin" "github.com/hermeznetwork/hermez-node/apitypes" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" ) @@ -49,7 +48,7 @@ func (a *API) getAccounts(c *gin.Context) { } // Fetch Accounts from historyDB - apiAccounts, pagination, err := a.h.GetAccountsAPI(tokenIDs, addr, bjj, fromItem, limit, order) + apiAccounts, pendingItems, err := a.h.GetAccountsAPI(tokenIDs, addr, bjj, fromItem, limit, order) if err != nil { retSQLErr(err, c) return @@ -72,11 +71,11 @@ func (a *API) getAccounts(c *gin.Context) { // Build succesfull response type accountResponse struct { - Accounts []historydb.AccountAPI `json:"accounts"` - Pagination *db.Pagination `json:"pagination"` + Accounts []historydb.AccountAPI `json:"accounts"` + PendingItems uint64 `json:"pendingItems"` } c.JSON(http.StatusOK, &accountResponse{ - Accounts: apiAccounts, - Pagination: pagination, + Accounts: apiAccounts, + PendingItems: pendingItems, }) } diff --git a/api/account_test.go b/api/account_test.go index aaa53d2..e988144 100644 --- a/api/account_test.go +++ b/api/account_test.go @@ -7,7 +7,6 @@ import ( "github.com/hermeznetwork/hermez-node/apitypes" "github.com/hermeznetwork/hermez-node/common" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/mitchellh/copystructure" "github.com/stretchr/testify/assert" @@ -25,10 +24,18 @@ type testAccount struct { } type testAccountsResponse struct { - Accounts []testAccount `json:"accounts"` - Pagination *db.Pagination `json:"pagination"` + Accounts []testAccount `json:"accounts"` + PendingItems uint64 `json:"pendingItems"` } +func (t testAccountsResponse) GetPending() (pendingItems, lastItemID uint64) { + pendingItems = t.PendingItems + lastItemID = t.Accounts[len(t.Accounts)-1].ItemID + return pendingItems, lastItemID +} + +func (t *testAccountsResponse) Len() int { return len(t.Accounts) } + func genTestAccounts(accounts []common.Account, tokens []historydb.TokenWithUSD) []testAccount { tAccounts := []testAccount{} for x, account := range accounts { @@ -47,19 +54,6 @@ func genTestAccounts(accounts []common.Account, tokens []historydb.TokenWithUSD) return tAccounts } -func (t *testAccountsResponse) GetPagination() *db.Pagination { - if t.Accounts[0].ItemID < t.Accounts[len(t.Accounts)-1].ItemID { - t.Pagination.FirstReturnedItem = t.Accounts[0].ItemID - t.Pagination.LastReturnedItem = t.Accounts[len(t.Accounts)-1].ItemID - } else { - t.Pagination.LastReturnedItem = t.Accounts[0].ItemID - t.Pagination.FirstReturnedItem = t.Accounts[len(t.Accounts)-1].ItemID - } - return t.Pagination -} - -func (t *testAccountsResponse) Len() int { return len(t.Accounts) } - func TestGetAccounts(t *testing.T) { endpoint := apiURL + "accounts" fetchedAccounts := []testAccount{} diff --git a/api/api_test.go b/api/api_test.go index a718e39..e924d94 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -28,6 +28,13 @@ import ( "github.com/iden3/go-iden3-crypto/babyjub" ) +// Pendinger is an interface that allows getting last returned item ID and PendingItems to be used for building fromItem +// when testing paginated endpoints. +type Pendinger interface { + GetPending() (pendingItems, lastItemID uint64) + Len() int +} + const apiPort = ":4010" const apiURL = "http://localhost" + apiPort + "/" @@ -339,41 +346,52 @@ func TestMain(m *testing.M) { func doGoodReqPaginated( path, order string, - iterStruct db.Paginationer, + iterStruct Pendinger, appendIter func(res interface{}), ) error { - next := -1 + var next uint64 + firstIte := true + expectedTotal := 0 + totalReceived := 0 for { - // Call API to get this iteration items + // Calculate fromItem iterPath := path - if next == -1 && order == historydb.OrderDesc { - // Fetch first item in reverse order - iterPath += "99999" - } else { - // Fetch from next item or 0 if it's ascending order - if next == -1 { - next = 0 + if firstIte { + if order == historydb.OrderDesc { + // Fetch first item in reverse order + iterPath += "99999" // Asumption that for testing there won't be any itemID > 99999 + } else { + iterPath += "0" } - iterPath += strconv.Itoa(next) + } else { + iterPath += strconv.Itoa(int(next)) } + // Call API to get this iteration items if err := doGoodReq("GET", iterPath+"&order="+order, nil, iterStruct); err != nil { return err } appendIter(iterStruct) // Keep iterating? - pag := iterStruct.GetPagination() - if order == historydb.OrderAsc { - if pag.LastReturnedItem == pag.LastItem { // No - break - } else { // Yes - next = int(pag.LastReturnedItem + 1) - } + remaining, lastID := iterStruct.GetPending() + if remaining == 0 { + break + } + if order == historydb.OrderDesc { + next = lastID - 1 } else { - if pag.FirstReturnedItem == pag.FirstItem { // No - break - } else { // Yes - next = int(pag.FirstReturnedItem - 1) - } + next = lastID + 1 + } + // Check that the expected amount of items is consistent across iterations + totalReceived += iterStruct.Len() + if firstIte { + firstIte = false + expectedTotal = totalReceived + int(remaining) + } + if expectedTotal != totalReceived+int(remaining) { + panic(fmt.Sprintf( + "pagination error, totalReceived + remaining should be %d, but is %d", + expectedTotal, totalReceived+int(remaining), + )) } } return nil diff --git a/api/batch.go b/api/batch.go index c87c9ea..c41f4d5 100644 --- a/api/batch.go +++ b/api/batch.go @@ -6,7 +6,6 @@ import ( "github.com/gin-gonic/gin" "github.com/hermeznetwork/hermez-node/common" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" ) @@ -43,7 +42,7 @@ func (a *API) getBatches(c *gin.Context) { return } // Fetch batches from historyDB - batches, pagination, err := a.h.GetBatchesAPI( + batches, pendingItems, err := a.h.GetBatchesAPI( minBatchNum, maxBatchNum, slotNum, forgerAddr, fromItem, limit, order, ) if err != nil { @@ -53,12 +52,12 @@ func (a *API) getBatches(c *gin.Context) { // Build succesfull response type batchesResponse struct { - Batches []historydb.BatchAPI `json:"batches"` - Pagination *db.Pagination `json:"pagination"` + Batches []historydb.BatchAPI `json:"batches"` + PendingItems uint64 `json:"pendingItems"` } c.JSON(http.StatusOK, &batchesResponse{ - Batches: batches, - Pagination: pagination, + Batches: batches, + PendingItems: pendingItems, }) } diff --git a/api/batch_test.go b/api/batch_test.go index aba12e3..135a4f9 100644 --- a/api/batch_test.go +++ b/api/batch_test.go @@ -8,7 +8,6 @@ import ( ethCommon "github.com/ethereum/go-ethereum/common" "github.com/hermeznetwork/hermez-node/common" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/mitchellh/copystructure" "github.com/stretchr/testify/assert" @@ -30,19 +29,14 @@ type testBatch struct { SlotNum int64 `json:"slotNum"` } type testBatchesResponse struct { - Batches []testBatch `json:"batches"` - Pagination *db.Pagination `json:"pagination"` + Batches []testBatch `json:"batches"` + PendingItems uint64 `json:"pendingItems"` } -func (t testBatchesResponse) GetPagination() *db.Pagination { - if t.Batches[0].ItemID < t.Batches[len(t.Batches)-1].ItemID { - t.Pagination.FirstReturnedItem = t.Batches[0].ItemID - t.Pagination.LastReturnedItem = t.Batches[len(t.Batches)-1].ItemID - } else { - t.Pagination.LastReturnedItem = t.Batches[0].ItemID - t.Pagination.FirstReturnedItem = t.Batches[len(t.Batches)-1].ItemID - } - return t.Pagination +func (t testBatchesResponse) GetPending() (pendingItems, lastItemID uint64) { + pendingItems = t.PendingItems + lastItemID = t.Batches[len(t.Batches)-1].ItemID + return pendingItems, lastItemID } func (t testBatchesResponse) Len() int { diff --git a/api/bids.go b/api/bids.go index a121845..cdfa89c 100644 --- a/api/bids.go +++ b/api/bids.go @@ -5,7 +5,6 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" ) @@ -26,7 +25,7 @@ func (a *API) getBids(c *gin.Context) { return } - bids, pagination, err := a.h.GetBidsAPI( + bids, pendingItems, err := a.h.GetBidsAPI( slotNum, bidderAddr, fromItem, limit, order, ) @@ -37,11 +36,11 @@ func (a *API) getBids(c *gin.Context) { // Build succesfull response type bidsResponse struct { - Bids []historydb.BidAPI `json:"bids"` - Pagination *db.Pagination `json:"pagination"` + Bids []historydb.BidAPI `json:"bids"` + PendingItems uint64 `json:"pendingItems"` } c.JSON(http.StatusOK, &bidsResponse{ - Bids: bids, - Pagination: pagination, + Bids: bids, + PendingItems: pendingItems, }) } diff --git a/api/bids_test.go b/api/bids_test.go index f02914d..7813988 100644 --- a/api/bids_test.go +++ b/api/bids_test.go @@ -7,7 +7,6 @@ import ( ethCommon "github.com/ethereum/go-ethereum/common" "github.com/hermeznetwork/hermez-node/common" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/mitchellh/copystructure" "github.com/stretchr/testify/assert" @@ -25,19 +24,14 @@ type testBid struct { } type testBidsResponse struct { - Bids []testBid `json:"bids"` - Pagination *db.Pagination `json:"pagination"` + Bids []testBid `json:"bids"` + PendingItems uint64 `json:"pendingItems"` } -func (t testBidsResponse) GetPagination() *db.Pagination { - if t.Bids[0].ItemID < t.Bids[len(t.Bids)-1].ItemID { - t.Pagination.FirstReturnedItem = t.Bids[0].ItemID - t.Pagination.LastReturnedItem = t.Bids[len(t.Bids)-1].ItemID - } else { - t.Pagination.LastReturnedItem = t.Bids[0].ItemID - t.Pagination.FirstReturnedItem = t.Bids[len(t.Bids)-1].ItemID - } - return t.Pagination +func (t testBidsResponse) GetPending() (pendingItems, lastItemID uint64) { + pendingItems = t.PendingItems + lastItemID = t.Bids[len(t.Bids)-1].ItemID + return pendingItems, lastItemID } func (t testBidsResponse) Len() int { diff --git a/api/coordinator.go b/api/coordinator.go index 6a4f7d8..1471457 100644 --- a/api/coordinator.go +++ b/api/coordinator.go @@ -4,7 +4,6 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" ) @@ -39,7 +38,7 @@ func (a *API) getCoordinators(c *gin.Context) { } // Fetch coordinators from historyDB - coordinators, pagination, err := a.h.GetCoordinatorsAPI(fromItem, limit, order) + coordinators, pendingItems, err := a.h.GetCoordinatorsAPI(fromItem, limit, order) if err != nil { retSQLErr(err, c) return @@ -48,10 +47,10 @@ func (a *API) getCoordinators(c *gin.Context) { // Build succesfull response type coordinatorsResponse struct { Coordinators []historydb.CoordinatorAPI `json:"coordinators"` - Pagination *db.Pagination `json:"pagination"` + PendingItems uint64 `json:"pendingItems"` } c.JSON(http.StatusOK, &coordinatorsResponse{ Coordinators: coordinators, - Pagination: pagination, + PendingItems: pendingItems, }) } diff --git a/api/coordinator_test.go b/api/coordinator_test.go index 97cbc99..822687b 100644 --- a/api/coordinator_test.go +++ b/api/coordinator_test.go @@ -4,7 +4,6 @@ import ( "fmt" "testing" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/mitchellh/copystructure" "github.com/stretchr/testify/assert" @@ -12,18 +11,13 @@ import ( type testCoordinatorsResponse struct { Coordinators []historydb.CoordinatorAPI `json:"coordinators"` - Pagination *db.Pagination `json:"pagination"` + PendingItems uint64 `json:"pendingItems"` } -func (t *testCoordinatorsResponse) GetPagination() *db.Pagination { - if t.Coordinators[0].ItemID < t.Coordinators[len(t.Coordinators)-1].ItemID { - t.Pagination.FirstReturnedItem = t.Coordinators[0].ItemID - t.Pagination.LastReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID - } else { - t.Pagination.LastReturnedItem = t.Coordinators[0].ItemID - t.Pagination.FirstReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID - } - return t.Pagination +func (t testCoordinatorsResponse) GetPending() (pendingItems, lastItemID uint64) { + pendingItems = t.PendingItems + lastItemID = t.Coordinators[len(t.Coordinators)-1].ItemID + return pendingItems, lastItemID } func (t *testCoordinatorsResponse) Len() int { return len(t.Coordinators) } diff --git a/api/exits.go b/api/exits.go index c4ad8d7..d86c70d 100644 --- a/api/exits.go +++ b/api/exits.go @@ -4,7 +4,6 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" ) @@ -36,7 +35,7 @@ func (a *API) getExits(c *gin.Context) { } // Fetch exits from historyDB - exits, pagination, err := a.h.GetExitsAPI( + exits, pendingItems, err := a.h.GetExitsAPI( addr, bjj, tokenID, idx, batchNum, onlyPendingWithdraws, fromItem, limit, order, ) if err != nil { @@ -46,12 +45,12 @@ func (a *API) getExits(c *gin.Context) { // Build succesfull response type exitsResponse struct { - Exits []historydb.ExitAPI `json:"exits"` - Pagination *db.Pagination `json:"pagination"` + Exits []historydb.ExitAPI `json:"exits"` + PendingItems uint64 `json:"pendingItems"` } c.JSON(http.StatusOK, &exitsResponse{ - Exits: exits, - Pagination: pagination, + Exits: exits, + PendingItems: pendingItems, }) } diff --git a/api/exits_test.go b/api/exits_test.go index 1e4d181..e6ef8bb 100644 --- a/api/exits_test.go +++ b/api/exits_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/hermeznetwork/hermez-node/common" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/mitchellh/copystructure" "github.com/stretchr/testify/assert" @@ -36,19 +35,14 @@ type testExit struct { } type testExitsResponse struct { - Exits []testExit `json:"exits"` - Pagination *db.Pagination `json:"pagination"` + Exits []testExit `json:"exits"` + PendingItems uint64 `json:"pendingItems"` } -func (t *testExitsResponse) GetPagination() *db.Pagination { - if t.Exits[0].ItemID < t.Exits[len(t.Exits)-1].ItemID { - t.Pagination.FirstReturnedItem = t.Exits[0].ItemID - t.Pagination.LastReturnedItem = t.Exits[len(t.Exits)-1].ItemID - } else { - t.Pagination.LastReturnedItem = t.Exits[0].ItemID - t.Pagination.FirstReturnedItem = t.Exits[len(t.Exits)-1].ItemID - } - return t.Pagination +func (t testExitsResponse) GetPending() (pendingItems, lastItemID uint64) { + pendingItems = t.PendingItems + lastItemID = t.Exits[len(t.Exits)-1].ItemID + return pendingItems, lastItemID } func (t *testExitsResponse) Len() int { diff --git a/api/slots.go b/api/slots.go index 9d7d1e6..3d2a3e2 100644 --- a/api/slots.go +++ b/api/slots.go @@ -7,7 +7,6 @@ import ( "github.com/gin-gonic/gin" "github.com/hermeznetwork/hermez-node/common" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" ) @@ -49,18 +48,6 @@ func (a *API) isOpenAuction(currentBlock, slotNum int64, auctionVars common.Auct return false } -func getPagination(totalItems uint64, minSlotNum, maxSlotNum *int64) *db.Pagination { - // itemID is slotNum - firstItem := *minSlotNum - lastItem := *maxSlotNum - pagination := &db.Pagination{ - TotalItems: totalItems, - FirstItem: uint64(firstItem), - LastItem: uint64(lastItem), - } - return pagination -} - func (a *API) newSlotAPI(slotNum, currentBlockNum int64, bid *historydb.BidAPI, auctionVars *common.AuctionVariables) SlotAPI { firstBlock, lastBlock := a.getFirstLastBlock(slotNum) openAuction := a.isOpenAuction(currentBlockNum, slotNum, *auctionVars) @@ -141,34 +128,35 @@ func (a *API) getSlot(c *gin.Context) { c.JSON(http.StatusOK, slot) } -func getLimits(minSlotNum, maxSlotNum *int64, fromItem, limit *uint, order string) (int64, int64) { - var minLim, maxLim int64 - if fromItem != nil { - if order == historydb.OrderAsc { - if int64(*fromItem) > *minSlotNum { - minLim = int64(*fromItem) - } else { - minLim = *minSlotNum - } - if (minLim + int64(*limit-1)) < *maxSlotNum { - maxLim = minLim + int64(*limit-1) - } else { - maxLim = *maxSlotNum - } +func getLimits( + minSlotNum, maxSlotNum int64, fromItem, limit *uint, order string, +) (minLimit, maxLimit int64, pendingItems uint64) { + if order == historydb.OrderAsc { + if fromItem != nil && int64(*fromItem) > minSlotNum { + minLimit = int64(*fromItem) } else { - if int64(*fromItem) < *maxSlotNum { - maxLim = int64(*fromItem) - } else { - maxLim = *maxSlotNum - } - if (maxLim - int64(*limit-1)) < *minSlotNum { - minLim = *minSlotNum - } else { - minLim = maxLim - int64(*limit-1) - } + minLimit = minSlotNum + } + if limit != nil && (minLimit+int64(*limit-1)) < maxSlotNum { + maxLimit = minLimit + int64(*limit-1) + } else { + maxLimit = maxSlotNum + } + pendingItems = uint64(maxSlotNum - maxLimit) + } else { + if fromItem != nil && int64(*fromItem) < maxSlotNum { + maxLimit = int64(*fromItem) + } else { + maxLimit = maxSlotNum } + if limit != nil && (maxLimit-int64(*limit-1)) < minSlotNum { + minLimit = minSlotNum + } else { + minLimit = maxLimit - int64(*limit-1) + } + pendingItems = uint64(-(minSlotNum - minLimit)) } - return minLim, maxLim + return minLimit, maxLimit, pendingItems } func getLimitsWithAddr(minSlotNum, maxSlotNum *int64, fromItem, limit *uint, order string) (int64, int64) { @@ -262,29 +250,22 @@ func (a *API) getSlots(c *gin.Context) { // Get bids and pagination according to filters var slotMinLim, slotMaxLim int64 var bids []historydb.BidAPI - var pag *db.Pagination - totalItems := uint64(0) + var pendingItems uint64 if wonByEthereumAddress == nil { - slotMinLim, slotMaxLim = getLimits(minSlotNum, maxSlotNum, fromItem, limit, order) + slotMinLim, slotMaxLim, pendingItems = getLimits(*minSlotNum, *maxSlotNum, fromItem, limit, order) // Get best bids in range maxSlotNum - minSlotNum bids, _, err = a.h.GetBestBidsAPI(&slotMinLim, &slotMaxLim, wonByEthereumAddress, nil, order) if err != nil && err != sql.ErrNoRows { retSQLErr(err, c) return } - totalItems = uint64(*maxSlotNum) - uint64(*minSlotNum) + 1 } else { slotMinLim, slotMaxLim = getLimitsWithAddr(minSlotNum, maxSlotNum, fromItem, limit, order) - bids, pag, err = a.h.GetBestBidsAPI(&slotMinLim, &slotMaxLim, wonByEthereumAddress, limit, order) + bids, pendingItems, err = a.h.GetBestBidsAPI(&slotMinLim, &slotMaxLim, wonByEthereumAddress, limit, order) if err != nil && err != sql.ErrNoRows { retSQLErr(err, c) return } - if len(bids) > 0 { - totalItems = uint64(pag.TotalItems) - *maxSlotNum = int64(pag.LastItem) - *minSlotNum = int64(pag.FirstItem) - } } // Build the slot information with previous bids @@ -336,11 +317,11 @@ func (a *API) getSlots(c *gin.Context) { // Build succesfull response type slotsResponse struct { - Slots []SlotAPI `json:"slots"` - Pagination *db.Pagination `json:"pagination"` + Slots []SlotAPI `json:"slots"` + PendingItems uint64 `json:"pendingItems"` } c.JSON(http.StatusOK, &slotsResponse{ - Slots: slots, - Pagination: getPagination(totalItems, minSlotNum, maxSlotNum), + Slots: slots, + PendingItems: pendingItems, }) } diff --git a/api/slots_test.go b/api/slots_test.go index 781a11b..54c35e7 100644 --- a/api/slots_test.go +++ b/api/slots_test.go @@ -6,14 +6,13 @@ import ( "testing" "github.com/hermeznetwork/hermez-node/common" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/mitchellh/copystructure" "github.com/stretchr/testify/assert" ) type testSlot struct { - ItemID int `json:"itemId"` + ItemID uint64 `json:"itemId"` SlotNum int64 `json:"slotNum"` FirstBlock int64 `json:"firstBlock"` LastBlock int64 `json:"lastBlock"` @@ -22,19 +21,14 @@ type testSlot struct { } type testSlotsResponse struct { - Slots []testSlot `json:"slots"` - Pagination *db.Pagination `json:"pagination"` + Slots []testSlot `json:"slots"` + PendingItems uint64 `json:"pendingItems"` } -func (t testSlotsResponse) GetPagination() *db.Pagination { - if t.Slots[0].ItemID < t.Slots[len(t.Slots)-1].ItemID { - t.Pagination.FirstReturnedItem = uint64(t.Slots[0].ItemID) - t.Pagination.LastReturnedItem = uint64(t.Slots[len(t.Slots)-1].ItemID) - } else { - t.Pagination.LastReturnedItem = uint64(t.Slots[0].ItemID) - t.Pagination.FirstReturnedItem = uint64(t.Slots[len(t.Slots)-1].ItemID) - } - return t.Pagination +func (t testSlotsResponse) GetPending() (pendingItems, lastItemID uint64) { + pendingItems = t.PendingItems + lastItemID = t.Slots[len(t.Slots)-1].ItemID + return pendingItems, lastItemID } func (t testSlotsResponse) Len() int { diff --git a/api/swagger.yml b/api/swagger.yml index 0e4e4c5..fbb2af2 100644 --- a/api/swagger.yml +++ b/api/swagger.yml @@ -17,7 +17,9 @@ info: * `order`: all pginated items are ordered chronologicaly. However the specific fields to guarantee this order depend on each endpoint. For this purpose, `itemId` is used (itemId follows ascending chronological order except for unforged L1 user transactions). If the parameter is not provided, ascending order will be used by default. * `limit`: maximum amount of items to include in each response. Default is 20, maximum 2049. - Responses for those endpoint will always include a `pagination` object. This object includes the total amount of items that the endpoint will return at a given time with the given filters. Apart from that, it also includes the `itemId` of the last and first items that will be returned (not in a single response but within the total items). These two properties can be used to know when to stop querying. + Responses for those endpoint will always include a `pendingItems` property. This property includes the amount of items that are not fetched yet. his can be used to: + * Calculate the amount of items that match the filters: `totalItems = length(alreadyFetchedItems) + pendingItems` + * Know when all items have been fetched: `if pendingItems == 0 {/* all items have been fetched */}` #### Reorgs and safetyness @@ -1739,11 +1741,11 @@ components: description: List of history transactions. items: $ref: '#/components/schemas/HistoryTransaction' - pagination: - $ref: '#/components/schemas/PaginationInfo' + pendingItems: + $ref: '#/components/schemas/PendingItems' required: - transactions - - pagination + - pendingItems additionalProperties: false EthBlockNum: type: integer @@ -1877,12 +1879,12 @@ components: description: List of batches. items: $ref: '#/components/schemas/Batch' - pagination: - $ref: '#/components/schemas/PaginationInfo' + pendingItems: + $ref: '#/components/schemas/PendingItems' additionalProperties: false required: - batches - - pagination + - pendingItems Coordinator: type: object properties: @@ -1914,12 +1916,12 @@ components: description: List of coordinators. items: $ref: '#/components/schemas/Coordinator' - pagination: - $ref: '#/components/schemas/PaginationInfo' + pendingItems: + $ref: '#/components/schemas/PendingItems' additionalProperties: false required: - coordinators - - pagination + - pendingItems Bid: type: object description: Tokens placed in an auction by a coordinator to gain the right to forge batches during a specific slot. @@ -1958,12 +1960,12 @@ components: description: List of bids. items: $ref: '#/components/schemas/Bid' - pagination: - $ref: '#/components/schemas/PaginationInfo' + pendingItems: + $ref: '#/components/schemas/PendingItems' additionalProperties: false require: - bids - - pagination + - pendingItems RecommendedFee: type: object description: Fee that the coordinator recommends per transaction in USD. @@ -2042,8 +2044,8 @@ components: description: List of tokens. items: $ref: '#/components/schemas/Token' - pagination: - $ref: '#/components/schemas/PaginationInfo' + pendingItems: + $ref: '#/components/schemas/PendingItems' Exit: type: object description: Exit tree leaf. It Contains the necessary information to perform a withdrawal. @@ -2135,11 +2137,11 @@ components: description: List of exits. items: $ref: '#/components/schemas/Exit' - pagination: - $ref: '#/components/schemas/PaginationInfo' + pendingItems: + $ref: '#/components/schemas/PendingItems' required: - exits - - pagination + - pendingItems additionalProperties: false Account: type: object @@ -2176,12 +2178,12 @@ components: description: List of accounts. items: $ref: '#/components/schemas/Account' - pagination: - $ref: '#/components/schemas/PaginationInfo' + pendingItems: + $ref: '#/components/schemas/PendingItems' additionalProperties: false required: - accounts - - pagination + - pendingItems Slot: type: object description: Slot information. @@ -2255,12 +2257,12 @@ components: allOf: - $ref: '#/components/schemas/Slot' - description: Last synchronized Etherum block. - pagination: - $ref: '#/components/schemas/PaginationInfo' + pendingItems: + $ref: '#/components/schemas/PendingItems' additionalProperties: false require: - slots - - pagination + - pendingItems NextForger: type: object description: Coordinator information along with the scheduled forging period @@ -2289,8 +2291,8 @@ components: description: List of next coordinators to forge. items: $ref: '#/components/schemas/NextForger' - pagination: - $ref: '#/components/schemas/PaginationInfo' + pendingItems: + $ref: '#/components/schemas/PendingItems' State: type: object description: Gobal variables of the network @@ -2517,22 +2519,10 @@ components: - auction - withdrawalDelayer - recomendedFee - PaginationInfo: - type: object - description: Give pagination information - properties: - totalItems: - type: integer - description: Amount of items that the endpoint can return given the filters and the current state of the database. - example: 2048 - firstItem: - type: integer - description: The smallest itemId that the endpoint will return with the given filters. - example: 50 - lastItem: - type: integer - description: The greatest itemId that the endpoint will return with the given filters. - example: 2130 + PendingItems: + type: integer + description: Amount of items that will be returned in subsequent calls to the endpoint, as long as they are done with same filters. When the value is 0 it means that all items have been sent. + example: 15 Config: type: object description: Configuration parameters of the different smart contracts that power the Hermez network. diff --git a/api/token.go b/api/token.go index 5719c44..00d0f65 100644 --- a/api/token.go +++ b/api/token.go @@ -6,7 +6,6 @@ import ( "github.com/gin-gonic/gin" "github.com/hermeznetwork/hermez-node/common" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" ) @@ -46,7 +45,7 @@ func (a *API) getTokens(c *gin.Context) { return } // Fetch exits from historyDB - tokens, pagination, err := a.h.GetTokens( + tokens, pendingItems, err := a.h.GetTokens( tokenIDs, symbols, name, fromItem, limit, order, ) if err != nil { @@ -56,11 +55,11 @@ func (a *API) getTokens(c *gin.Context) { // Build succesfull response type tokensResponse struct { - Tokens []historydb.TokenWithUSD `json:"tokens"` - Pagination *db.Pagination `json:"pagination"` + Tokens []historydb.TokenWithUSD `json:"tokens"` + PendingItems uint64 `json:"pendingItems"` } c.JSON(http.StatusOK, &tokensResponse{ - Tokens: tokens, - Pagination: pagination, + Tokens: tokens, + PendingItems: pendingItems, }) } diff --git a/api/token_test.go b/api/token_test.go index 732a3c6..47ad26b 100644 --- a/api/token_test.go +++ b/api/token_test.go @@ -5,7 +5,6 @@ import ( "strconv" "testing" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/mitchellh/copystructure" "github.com/stretchr/testify/assert" @@ -13,19 +12,14 @@ import ( ) type testTokensResponse struct { - Tokens []historydb.TokenWithUSD `json:"tokens"` - Pagination *db.Pagination `json:"pagination"` + Tokens []historydb.TokenWithUSD `json:"tokens"` + PendingItems uint64 `json:"pendingItems"` } -func (t *testTokensResponse) GetPagination() *db.Pagination { - if t.Tokens[0].ItemID < t.Tokens[len(t.Tokens)-1].ItemID { - t.Pagination.FirstReturnedItem = t.Tokens[0].ItemID - t.Pagination.LastReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID - } else { - t.Pagination.LastReturnedItem = t.Tokens[0].ItemID - t.Pagination.FirstReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID - } - return t.Pagination +func (t testTokensResponse) GetPending() (pendingItems, lastItemID uint64) { + pendingItems = t.PendingItems + lastItemID = t.Tokens[len(t.Tokens)-1].ItemID + return pendingItems, lastItemID } func (t *testTokensResponse) Len() int { diff --git a/api/txshistory.go b/api/txshistory.go index 7ede499..d4e889f 100644 --- a/api/txshistory.go +++ b/api/txshistory.go @@ -4,7 +4,6 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" ) @@ -35,7 +34,7 @@ func (a *API) getHistoryTxs(c *gin.Context) { } // Fetch txs from historyDB - txs, pagination, err := a.h.GetHistoryTxs( + txs, pendingItems, err := a.h.GetHistoryTxs( addr, bjj, tokenID, idx, batchNum, txType, fromItem, limit, order, ) if err != nil { @@ -45,12 +44,12 @@ func (a *API) getHistoryTxs(c *gin.Context) { // Build succesfull response type txsResponse struct { - Txs []historydb.TxAPI `json:"transactions"` - Pagination *db.Pagination `json:"pagination"` + Txs []historydb.TxAPI `json:"transactions"` + PendingItems uint64 `json:"pendingItems"` } c.JSON(http.StatusOK, &txsResponse{ - Txs: txs, - Pagination: pagination, + Txs: txs, + PendingItems: pendingItems, }) } diff --git a/api/txshistory_test.go b/api/txshistory_test.go index beb98de..1fd3831 100644 --- a/api/txshistory_test.go +++ b/api/txshistory_test.go @@ -9,7 +9,6 @@ import ( "github.com/hermeznetwork/hermez-node/apitypes" "github.com/hermeznetwork/hermez-node/common" - "github.com/hermeznetwork/hermez-node/db" "github.com/hermeznetwork/hermez-node/db/historydb" "github.com/hermeznetwork/hermez-node/test" "github.com/mitchellh/copystructure" @@ -53,19 +52,14 @@ type testTx struct { } type testTxsResponse struct { - Txs []testTx `json:"transactions"` - Pagination *db.Pagination `json:"pagination"` + Txs []testTx `json:"transactions"` + PendingItems uint64 `json:"pendingItems"` } -func (t testTxsResponse) GetPagination() *db.Pagination { - if t.Txs[0].ItemID < t.Txs[len(t.Txs)-1].ItemID { - t.Pagination.FirstReturnedItem = t.Txs[0].ItemID - t.Pagination.LastReturnedItem = t.Txs[len(t.Txs)-1].ItemID - } else { - t.Pagination.LastReturnedItem = t.Txs[0].ItemID - t.Pagination.FirstReturnedItem = t.Txs[len(t.Txs)-1].ItemID - } - return t.Pagination +func (t testTxsResponse) GetPending() (pendingItems, lastItemID uint64) { + pendingItems = t.PendingItems + lastItemID = t.Txs[len(t.Txs)-1].ItemID + return pendingItems, lastItemID } func (t testTxsResponse) Len() int { diff --git a/db/historydb/historydb.go b/db/historydb/historydb.go index bc9655f..011f9ec 100644 --- a/db/historydb/historydb.go +++ b/db/historydb/historydb.go @@ -181,12 +181,11 @@ func (hdb *HistoryDB) GetBatchesAPI( minBatchNum, maxBatchNum, slotNum *uint, forgerAddr *ethCommon.Address, fromItem, limit *uint, order string, -) ([]BatchAPI, *db.Pagination, error) { +) ([]BatchAPI, uint64, error) { var query string var args []interface{} queryStr := `SELECT batch.*, block.timestamp, block.hash, - count(*) OVER() AS total_items, MIN(batch.item_id) OVER() AS first_item, - MAX(batch.item_id) OVER() AS last_item + count(*) OVER() AS total_items FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num ` // Apply filters nextIsAnd := false @@ -259,17 +258,13 @@ func (hdb *HistoryDB) GetBatchesAPI( // log.Debug(query) batchPtrs := []*BatchAPI{} if err := meddler.QueryAll(hdb.db, &batchPtrs, query, args...); err != nil { - return nil, nil, err + return nil, 0, err } batches := db.SlicePtrsToSlice(batchPtrs).([]BatchAPI) if len(batches) == 0 { - return nil, nil, sql.ErrNoRows + return nil, 0, sql.ErrNoRows } - return batches, &db.Pagination{ - TotalItems: batches[0].TotalItems, - FirstItem: batches[0].FirstItem, - LastItem: batches[0].LastItem, - }, nil + return batches, batches[0].TotalItems - uint64(len(batches)), nil } // GetAllBatches retrieve all batches from the DB @@ -367,12 +362,15 @@ func (hdb *HistoryDB) GetBestBidAPI(slotNum *int64) (BidAPI, error) { } // GetBestBidsAPI returns the best bid in specific slot by slotNum -func (hdb *HistoryDB) GetBestBidsAPI(minSlotNum, maxSlotNum *int64, bidderAddr *ethCommon.Address, limit *uint, order string) ([]BidAPI, *db.Pagination, error) { +func (hdb *HistoryDB) GetBestBidsAPI( + minSlotNum, maxSlotNum *int64, + bidderAddr *ethCommon.Address, + limit *uint, order string, +) ([]BidAPI, uint64, error) { var query string var args []interface{} queryStr := `SELECT b.*, block.timestamp, coordinator.forger_addr, coordinator.url, - COUNT(*) OVER() AS total_items, MIN(b.slot_num) OVER() AS first_item, - MAX(b.slot_num) OVER() AS last_item FROM ( + COUNT(*) OVER() AS total_items FROM ( SELECT slot_num, MAX(item_id) as maxitem FROM bid GROUP BY slot_num ) @@ -399,28 +397,26 @@ func (hdb *HistoryDB) GetBestBidsAPI(minSlotNum, maxSlotNum *int64, bidderAddr * query = hdb.db.Rebind(queryStr) bidPtrs := []*BidAPI{} if err := meddler.QueryAll(hdb.db, &bidPtrs, query, args...); err != nil { - return nil, nil, err + return nil, 0, err } // log.Debug(query) bids := db.SlicePtrsToSlice(bidPtrs).([]BidAPI) if len(bids) == 0 { - return nil, nil, sql.ErrNoRows + return nil, 0, sql.ErrNoRows } - return bids, &db.Pagination{ - TotalItems: bids[0].TotalItems, - FirstItem: bids[0].FirstItem, - LastItem: bids[0].LastItem, - }, nil + return bids, bids[0].TotalItems - uint64(len(bids)), nil } // GetBidsAPI return the bids applying the given filters -func (hdb *HistoryDB) GetBidsAPI(slotNum *int64, forgerAddr *ethCommon.Address, fromItem, limit *uint, order string) ([]BidAPI, *db.Pagination, error) { +func (hdb *HistoryDB) GetBidsAPI( + slotNum *int64, forgerAddr *ethCommon.Address, + fromItem, limit *uint, order string, +) ([]BidAPI, uint64, error) { var query string var args []interface{} queryStr := `SELECT bid.*, block.timestamp, coordinator.forger_addr, coordinator.url, - COUNT(*) OVER() AS total_items, MIN(bid.item_id) OVER() AS first_item, - MAX(bid.item_id) OVER() AS last_item FROM bid - INNER JOIN block ON bid.eth_block_num = block.eth_block_num + COUNT(*) OVER() AS total_items + FROM bid INNER JOIN block ON bid.eth_block_num = block.eth_block_num INNER JOIN coordinator ON bid.bidder_addr = coordinator.bidder_addr ` // Apply filters nextIsAnd := false @@ -469,21 +465,17 @@ func (hdb *HistoryDB) GetBidsAPI(slotNum *int64, forgerAddr *ethCommon.Address, queryStr += fmt.Sprintf("LIMIT %d;", *limit) query, argsQ, err := sqlx.In(queryStr, args...) if err != nil { - return nil, nil, err + return nil, 0, err } query = hdb.db.Rebind(query) bids := []*BidAPI{} if err := meddler.QueryAll(hdb.db, &bids, query, argsQ...); err != nil { - return nil, nil, err + return nil, 0, err } if len(bids) == 0 { - return nil, nil, sql.ErrNoRows + return nil, 0, sql.ErrNoRows } - return db.SlicePtrsToSlice(bids).([]BidAPI), &db.Pagination{ - TotalItems: bids[0].TotalItems, - FirstItem: bids[0].FirstItem, - LastItem: bids[0].LastItem, - }, nil + return db.SlicePtrsToSlice(bids).([]BidAPI), bids[0].TotalItems - uint64(len(bids)), nil } // AddCoordinators insert Coordinators into the DB @@ -562,10 +554,13 @@ func (hdb *HistoryDB) GetAllTokens() ([]TokenWithUSD, error) { } // GetTokens returns a list of tokens from the DB -func (hdb *HistoryDB) GetTokens(ids []common.TokenID, symbols []string, name string, fromItem, limit *uint, order string) ([]TokenWithUSD, *db.Pagination, error) { +func (hdb *HistoryDB) GetTokens( + ids []common.TokenID, symbols []string, name string, fromItem, + limit *uint, order string, +) ([]TokenWithUSD, uint64, error) { var query string var args []interface{} - queryStr := `SELECT * , COUNT(*) OVER() AS total_items, MIN(token.item_id) OVER() AS first_item, MAX(token.item_id) OVER() AS last_item FROM token ` + queryStr := `SELECT * , COUNT(*) OVER() AS total_items FROM token ` // Apply filters nextIsAnd := false if len(ids) > 0 { @@ -616,21 +611,17 @@ func (hdb *HistoryDB) GetTokens(ids []common.TokenID, symbols []string, name str queryStr += fmt.Sprintf("LIMIT %d;", *limit) query, argsQ, err := sqlx.In(queryStr, args...) if err != nil { - return nil, nil, err + return nil, 0, err } query = hdb.db.Rebind(query) tokens := []*TokenWithUSD{} if err := meddler.QueryAll(hdb.db, &tokens, query, argsQ...); err != nil { - return nil, nil, err + return nil, 0, err } if len(tokens) == 0 { - return nil, nil, sql.ErrNoRows + return nil, 0, sql.ErrNoRows } - return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), &db.Pagination{ - TotalItems: tokens[0].TotalItems, - FirstItem: tokens[0].FirstItem, - LastItem: tokens[0].LastItem, - }, nil + return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), uint64(len(tokens)) - tokens[0].TotalItems, nil } // GetTokenSymbols returns all the token symbols from the DB @@ -813,9 +804,9 @@ func (hdb *HistoryDB) GetHistoryTxs( ethAddr *ethCommon.Address, bjj *babyjub.PublicKey, tokenID *common.TokenID, idx *common.Idx, batchNum *uint, txType *common.TxType, fromItem, limit *uint, order string, -) ([]TxAPI, *db.Pagination, error) { +) ([]TxAPI, uint64, error) { if ethAddr != nil && bjj != nil { - return nil, nil, errors.New("ethAddr and bjj are incompatible") + return nil, 0, errors.New("ethAddr and bjj are incompatible") } var query string var args []interface{} @@ -827,8 +818,7 @@ func (hdb *HistoryDB) GetHistoryTxs( tx.load_amount, tx.load_amount_usd, tx.fee, tx.fee_usd, tx.nonce, token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block, token.eth_addr, token.name, token.symbol, token.decimals, token.usd, - token.usd_update, block.timestamp, count(*) OVER() AS total_items, - MIN(tx.item_id) OVER() AS first_item, MAX(tx.item_id) OVER() AS last_item + token.usd_update, block.timestamp, count(*) OVER() AS total_items FROM tx INNER JOIN token ON tx.token_id = token.token_id INNER JOIN block ON tx.eth_block_num = block.eth_block_num ` // Apply filters @@ -924,17 +914,13 @@ func (hdb *HistoryDB) GetHistoryTxs( // log.Debug(query) txsPtrs := []*TxAPI{} if err := meddler.QueryAll(hdb.db, &txsPtrs, query, args...); err != nil { - return nil, nil, err + return nil, 0, err } txs := db.SlicePtrsToSlice(txsPtrs).([]TxAPI) if len(txs) == 0 { - return nil, nil, sql.ErrNoRows + return nil, 0, sql.ErrNoRows } - return txs, &db.Pagination{ - TotalItems: txs[0].TotalItems, - FirstItem: txs[0].FirstItem, - LastItem: txs[0].LastItem, - }, nil + return txs, txs[0].TotalItems - uint64(len(txs)), nil } // GetAllExits returns all exit from the DB @@ -972,9 +958,9 @@ func (hdb *HistoryDB) GetExitsAPI( ethAddr *ethCommon.Address, bjj *babyjub.PublicKey, tokenID *common.TokenID, idx *common.Idx, batchNum *uint, onlyPendingWithdraws *bool, fromItem, limit *uint, order string, -) ([]ExitAPI, *db.Pagination, error) { +) ([]ExitAPI, uint64, error) { if ethAddr != nil && bjj != nil { - return nil, nil, errors.New("ethAddr and bjj are incompatible") + return nil, 0, errors.New("ethAddr and bjj are incompatible") } var query string var args []interface{} @@ -984,8 +970,7 @@ func (hdb *HistoryDB) GetExitsAPI( exit_tree.delayed_withdraw_request, exit_tree.delayed_withdrawn, token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block, token.eth_addr, token.name, token.symbol, - token.decimals, token.usd, token.usd_update, COUNT(*) OVER() AS total_items, - MIN(exit_tree.item_id) OVER() AS first_item, MAX(exit_tree.item_id) OVER() AS last_item + token.decimals, token.usd, token.usd_update, COUNT(*) OVER() AS total_items FROM exit_tree INNER JOIN account ON exit_tree.account_idx = account.idx INNER JOIN token ON account.token_id = token.token_id ` // Apply filters @@ -1071,16 +1056,12 @@ func (hdb *HistoryDB) GetExitsAPI( // log.Debug(query) exits := []*ExitAPI{} if err := meddler.QueryAll(hdb.db, &exits, query, args...); err != nil { - return nil, nil, err + return nil, 0, err } if len(exits) == 0 { - return nil, nil, sql.ErrNoRows + return nil, 0, sql.ErrNoRows } - return db.SlicePtrsToSlice(exits).([]ExitAPI), &db.Pagination{ - TotalItems: exits[0].TotalItems, - FirstItem: exits[0].FirstItem, - LastItem: exits[0].LastItem, - }, nil + return db.SlicePtrsToSlice(exits).([]ExitAPI), exits[0].TotalItems - uint64(len(exits)), nil } // // GetTx returns a tx from the DB @@ -1337,11 +1318,11 @@ func (hdb *HistoryDB) GetCoordinatorAPI(bidderAddr ethCommon.Address) (*Coordina } // GetCoordinatorsAPI returns a list of coordinators from the DB and pagination info -func (hdb *HistoryDB) GetCoordinatorsAPI(fromItem, limit *uint, order string) ([]CoordinatorAPI, *db.Pagination, error) { +func (hdb *HistoryDB) GetCoordinatorsAPI(fromItem, limit *uint, order string) ([]CoordinatorAPI, uint64, error) { var query string var args []interface{} queryStr := `SELECT coordinator.*, - COUNT(*) OVER() AS total_items, MIN(coordinator.item_id) OVER() AS first_item, MAX(coordinator.item_id) OVER() AS last_item + COUNT(*) OVER() AS total_items FROM coordinator ` // Apply filters if fromItem != nil { @@ -1365,16 +1346,13 @@ func (hdb *HistoryDB) GetCoordinatorsAPI(fromItem, limit *uint, order string) ([ coordinators := []*CoordinatorAPI{} if err := meddler.QueryAll(hdb.db, &coordinators, query, args...); err != nil { - return nil, nil, err + return nil, 0, err } if len(coordinators) == 0 { - return nil, nil, sql.ErrNoRows + return nil, 0, sql.ErrNoRows } - return db.SlicePtrsToSlice(coordinators).([]CoordinatorAPI), &db.Pagination{ - TotalItems: coordinators[0].TotalItems, - FirstItem: coordinators[0].FirstItem, - LastItem: coordinators[0].LastItem, - }, nil + return db.SlicePtrsToSlice(coordinators).([]CoordinatorAPI), + coordinators[0].TotalItems - uint64(len(coordinators)), nil } // AddAuctionVars insert auction vars into the DB @@ -1394,7 +1372,8 @@ func (hdb *HistoryDB) GetAuctionVars() (*common.AuctionVariables, error) { // GetAccountAPI returns an account by its index func (hdb *HistoryDB) GetAccountAPI(idx common.Idx) (*AccountAPI, error) { account := &AccountAPI{} - err := meddler.QueryRow(hdb.db, account, `SELECT account.item_id, hez_idx(account.idx, token.symbol) as idx, account.batch_num, account.bjj, account.eth_addr, + err := meddler.QueryRow(hdb.db, account, `SELECT account.item_id, hez_idx(account.idx, + token.symbol) as idx, account.batch_num, account.bjj, account.eth_addr, token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block, token.eth_addr as token_eth_addr, token.name, token.symbol, token.decimals, token.usd, token.usd_update FROM account INNER JOIN token ON account.token_id = token.token_id WHERE idx = $1;`, idx) @@ -1407,16 +1386,19 @@ func (hdb *HistoryDB) GetAccountAPI(idx common.Idx) (*AccountAPI, error) { } // GetAccountsAPI returns a list of accounts from the DB and pagination info -func (hdb *HistoryDB) GetAccountsAPI(tokenIDs []common.TokenID, ethAddr *ethCommon.Address, bjj *babyjub.PublicKey, fromItem, limit *uint, order string) ([]AccountAPI, *db.Pagination, error) { +func (hdb *HistoryDB) GetAccountsAPI( + tokenIDs []common.TokenID, ethAddr *ethCommon.Address, + bjj *babyjub.PublicKey, fromItem, limit *uint, order string, +) ([]AccountAPI, uint64, error) { if ethAddr != nil && bjj != nil { - return nil, nil, errors.New("ethAddr and bjj are incompatible") + return nil, 0, errors.New("ethAddr and bjj are incompatible") } var query string var args []interface{} - queryStr := `SELECT account.item_id, hez_idx(account.idx, token.symbol) as idx, account.batch_num, account.bjj, account.eth_addr, - token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block, + queryStr := `SELECT account.item_id, hez_idx(account.idx, token.symbol) as idx, account.batch_num, + account.bjj, account.eth_addr, token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block, token.eth_addr as token_eth_addr, token.name, token.symbol, token.decimals, token.usd, token.usd_update, - COUNT(*) OVER() AS total_items, MIN(account.item_id) OVER() AS first_item, MAX(account.item_id) OVER() AS last_item + COUNT(*) OVER() AS total_items FROM account INNER JOIN token ON account.token_id = token.token_id ` // Apply filters nextIsAnd := false @@ -1464,21 +1446,18 @@ func (hdb *HistoryDB) GetAccountsAPI(tokenIDs []common.TokenID, ethAddr *ethComm queryStr += fmt.Sprintf("LIMIT %d;", *limit) query, argsQ, err := sqlx.In(queryStr, args...) if err != nil { - return nil, nil, err + return nil, 0, err } query = hdb.db.Rebind(query) accounts := []*AccountAPI{} if err := meddler.QueryAll(hdb.db, &accounts, query, argsQ...); err != nil { - return nil, nil, err + return nil, 0, err } if len(accounts) == 0 { - return nil, nil, sql.ErrNoRows + return nil, 0, sql.ErrNoRows } - return db.SlicePtrsToSlice(accounts).([]AccountAPI), &db.Pagination{ - TotalItems: accounts[0].TotalItems, - FirstItem: accounts[0].FirstItem, - LastItem: accounts[0].LastItem, - }, nil + return db.SlicePtrsToSlice(accounts).([]AccountAPI), + accounts[0].TotalItems - uint64(len(accounts)), nil } diff --git a/db/utils.go b/db/utils.go index 4909879..2e62c17 100644 --- a/db/utils.go +++ b/db/utils.go @@ -179,21 +179,6 @@ func SlicePtrsToSlice(slice interface{}) interface{} { return res.Interface() } -// Pagination give information on the items of a query -type Pagination struct { - TotalItems uint64 `json:"totalItems"` - FirstItem uint64 `json:"firstItem"` - LastItem uint64 `json:"lastItem"` - FirstReturnedItem uint64 `json:"-"` - LastReturnedItem uint64 `json:"-"` -} - -// Paginationer is an interface that allows getting pagination info on any struct -type Paginationer interface { - GetPagination() *Pagination - Len() int -} - // Rollback an sql transaction, and log the error if it's not nil func Rollback(txn *sql.Tx) { err := txn.Rollback()