mirror of
https://github.com/arnaucube/hermez-node.git
synced 2026-02-07 11:26:44 +01:00
Refactor api pagination
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/hermeznetwork/hermez-node/apitypes"
|
"github.com/hermeznetwork/hermez-node/apitypes"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ func (a *API) getAccounts(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch Accounts from historyDB
|
// 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 {
|
if err != nil {
|
||||||
retSQLErr(err, c)
|
retSQLErr(err, c)
|
||||||
return
|
return
|
||||||
@@ -72,11 +71,11 @@ func (a *API) getAccounts(c *gin.Context) {
|
|||||||
|
|
||||||
// Build succesfull response
|
// Build succesfull response
|
||||||
type accountResponse struct {
|
type accountResponse struct {
|
||||||
Accounts []historydb.AccountAPI `json:"accounts"`
|
Accounts []historydb.AccountAPI `json:"accounts"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, &accountResponse{
|
c.JSON(http.StatusOK, &accountResponse{
|
||||||
Accounts: apiAccounts,
|
Accounts: apiAccounts,
|
||||||
Pagination: pagination,
|
PendingItems: pendingItems,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/hermeznetwork/hermez-node/apitypes"
|
"github.com/hermeznetwork/hermez-node/apitypes"
|
||||||
"github.com/hermeznetwork/hermez-node/common"
|
"github.com/hermeznetwork/hermez-node/common"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
"github.com/mitchellh/copystructure"
|
"github.com/mitchellh/copystructure"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -25,10 +24,18 @@ type testAccount struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type testAccountsResponse struct {
|
type testAccountsResponse struct {
|
||||||
Accounts []testAccount `json:"accounts"`
|
Accounts []testAccount `json:"accounts"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
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 {
|
func genTestAccounts(accounts []common.Account, tokens []historydb.TokenWithUSD) []testAccount {
|
||||||
tAccounts := []testAccount{}
|
tAccounts := []testAccount{}
|
||||||
for x, account := range accounts {
|
for x, account := range accounts {
|
||||||
@@ -47,19 +54,6 @@ func genTestAccounts(accounts []common.Account, tokens []historydb.TokenWithUSD)
|
|||||||
return tAccounts
|
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) {
|
func TestGetAccounts(t *testing.T) {
|
||||||
endpoint := apiURL + "accounts"
|
endpoint := apiURL + "accounts"
|
||||||
fetchedAccounts := []testAccount{}
|
fetchedAccounts := []testAccount{}
|
||||||
|
|||||||
@@ -28,6 +28,13 @@ import (
|
|||||||
"github.com/iden3/go-iden3-crypto/babyjub"
|
"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 apiPort = ":4010"
|
||||||
const apiURL = "http://localhost" + apiPort + "/"
|
const apiURL = "http://localhost" + apiPort + "/"
|
||||||
|
|
||||||
@@ -339,41 +346,52 @@ func TestMain(m *testing.M) {
|
|||||||
|
|
||||||
func doGoodReqPaginated(
|
func doGoodReqPaginated(
|
||||||
path, order string,
|
path, order string,
|
||||||
iterStruct db.Paginationer,
|
iterStruct Pendinger,
|
||||||
appendIter func(res interface{}),
|
appendIter func(res interface{}),
|
||||||
) error {
|
) error {
|
||||||
next := -1
|
var next uint64
|
||||||
|
firstIte := true
|
||||||
|
expectedTotal := 0
|
||||||
|
totalReceived := 0
|
||||||
for {
|
for {
|
||||||
// Call API to get this iteration items
|
// Calculate fromItem
|
||||||
iterPath := path
|
iterPath := path
|
||||||
if next == -1 && order == historydb.OrderDesc {
|
if firstIte {
|
||||||
// Fetch first item in reverse order
|
if order == historydb.OrderDesc {
|
||||||
iterPath += "99999"
|
// Fetch first item in reverse order
|
||||||
} else {
|
iterPath += "99999" // Asumption that for testing there won't be any itemID > 99999
|
||||||
// Fetch from next item or 0 if it's ascending order
|
} else {
|
||||||
if next == -1 {
|
iterPath += "0"
|
||||||
next = 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 {
|
if err := doGoodReq("GET", iterPath+"&order="+order, nil, iterStruct); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
appendIter(iterStruct)
|
appendIter(iterStruct)
|
||||||
// Keep iterating?
|
// Keep iterating?
|
||||||
pag := iterStruct.GetPagination()
|
remaining, lastID := iterStruct.GetPending()
|
||||||
if order == historydb.OrderAsc {
|
if remaining == 0 {
|
||||||
if pag.LastReturnedItem == pag.LastItem { // No
|
break
|
||||||
break
|
}
|
||||||
} else { // Yes
|
if order == historydb.OrderDesc {
|
||||||
next = int(pag.LastReturnedItem + 1)
|
next = lastID - 1
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if pag.FirstReturnedItem == pag.FirstItem { // No
|
next = lastID + 1
|
||||||
break
|
}
|
||||||
} else { // Yes
|
// Check that the expected amount of items is consistent across iterations
|
||||||
next = int(pag.FirstReturnedItem - 1)
|
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
|
return nil
|
||||||
|
|||||||
11
api/batch.go
11
api/batch.go
@@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/hermeznetwork/hermez-node/common"
|
"github.com/hermeznetwork/hermez-node/common"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ func (a *API) getBatches(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Fetch batches from historyDB
|
// Fetch batches from historyDB
|
||||||
batches, pagination, err := a.h.GetBatchesAPI(
|
batches, pendingItems, err := a.h.GetBatchesAPI(
|
||||||
minBatchNum, maxBatchNum, slotNum, forgerAddr, fromItem, limit, order,
|
minBatchNum, maxBatchNum, slotNum, forgerAddr, fromItem, limit, order,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -53,12 +52,12 @@ func (a *API) getBatches(c *gin.Context) {
|
|||||||
|
|
||||||
// Build succesfull response
|
// Build succesfull response
|
||||||
type batchesResponse struct {
|
type batchesResponse struct {
|
||||||
Batches []historydb.BatchAPI `json:"batches"`
|
Batches []historydb.BatchAPI `json:"batches"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, &batchesResponse{
|
c.JSON(http.StatusOK, &batchesResponse{
|
||||||
Batches: batches,
|
Batches: batches,
|
||||||
Pagination: pagination,
|
PendingItems: pendingItems,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/hermeznetwork/hermez-node/common"
|
"github.com/hermeznetwork/hermez-node/common"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
"github.com/mitchellh/copystructure"
|
"github.com/mitchellh/copystructure"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -30,19 +29,14 @@ type testBatch struct {
|
|||||||
SlotNum int64 `json:"slotNum"`
|
SlotNum int64 `json:"slotNum"`
|
||||||
}
|
}
|
||||||
type testBatchesResponse struct {
|
type testBatchesResponse struct {
|
||||||
Batches []testBatch `json:"batches"`
|
Batches []testBatch `json:"batches"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testBatchesResponse) GetPagination() *db.Pagination {
|
func (t testBatchesResponse) GetPending() (pendingItems, lastItemID uint64) {
|
||||||
if t.Batches[0].ItemID < t.Batches[len(t.Batches)-1].ItemID {
|
pendingItems = t.PendingItems
|
||||||
t.Pagination.FirstReturnedItem = t.Batches[0].ItemID
|
lastItemID = t.Batches[len(t.Batches)-1].ItemID
|
||||||
t.Pagination.LastReturnedItem = t.Batches[len(t.Batches)-1].ItemID
|
return pendingItems, lastItemID
|
||||||
} else {
|
|
||||||
t.Pagination.LastReturnedItem = t.Batches[0].ItemID
|
|
||||||
t.Pagination.FirstReturnedItem = t.Batches[len(t.Batches)-1].ItemID
|
|
||||||
}
|
|
||||||
return t.Pagination
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testBatchesResponse) Len() int {
|
func (t testBatchesResponse) Len() int {
|
||||||
|
|||||||
11
api/bids.go
11
api/bids.go
@@ -5,7 +5,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,7 +25,7 @@ func (a *API) getBids(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bids, pagination, err := a.h.GetBidsAPI(
|
bids, pendingItems, err := a.h.GetBidsAPI(
|
||||||
slotNum, bidderAddr, fromItem, limit, order,
|
slotNum, bidderAddr, fromItem, limit, order,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,11 +36,11 @@ func (a *API) getBids(c *gin.Context) {
|
|||||||
|
|
||||||
// Build succesfull response
|
// Build succesfull response
|
||||||
type bidsResponse struct {
|
type bidsResponse struct {
|
||||||
Bids []historydb.BidAPI `json:"bids"`
|
Bids []historydb.BidAPI `json:"bids"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, &bidsResponse{
|
c.JSON(http.StatusOK, &bidsResponse{
|
||||||
Bids: bids,
|
Bids: bids,
|
||||||
Pagination: pagination,
|
PendingItems: pendingItems,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
ethCommon "github.com/ethereum/go-ethereum/common"
|
ethCommon "github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/hermeznetwork/hermez-node/common"
|
"github.com/hermeznetwork/hermez-node/common"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
"github.com/mitchellh/copystructure"
|
"github.com/mitchellh/copystructure"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -25,19 +24,14 @@ type testBid struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type testBidsResponse struct {
|
type testBidsResponse struct {
|
||||||
Bids []testBid `json:"bids"`
|
Bids []testBid `json:"bids"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testBidsResponse) GetPagination() *db.Pagination {
|
func (t testBidsResponse) GetPending() (pendingItems, lastItemID uint64) {
|
||||||
if t.Bids[0].ItemID < t.Bids[len(t.Bids)-1].ItemID {
|
pendingItems = t.PendingItems
|
||||||
t.Pagination.FirstReturnedItem = t.Bids[0].ItemID
|
lastItemID = t.Bids[len(t.Bids)-1].ItemID
|
||||||
t.Pagination.LastReturnedItem = t.Bids[len(t.Bids)-1].ItemID
|
return pendingItems, lastItemID
|
||||||
} else {
|
|
||||||
t.Pagination.LastReturnedItem = t.Bids[0].ItemID
|
|
||||||
t.Pagination.FirstReturnedItem = t.Bids[len(t.Bids)-1].ItemID
|
|
||||||
}
|
|
||||||
return t.Pagination
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testBidsResponse) Len() int {
|
func (t testBidsResponse) Len() int {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,7 +38,7 @@ func (a *API) getCoordinators(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch coordinators from historyDB
|
// 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 {
|
if err != nil {
|
||||||
retSQLErr(err, c)
|
retSQLErr(err, c)
|
||||||
return
|
return
|
||||||
@@ -48,10 +47,10 @@ func (a *API) getCoordinators(c *gin.Context) {
|
|||||||
// Build succesfull response
|
// Build succesfull response
|
||||||
type coordinatorsResponse struct {
|
type coordinatorsResponse struct {
|
||||||
Coordinators []historydb.CoordinatorAPI `json:"coordinators"`
|
Coordinators []historydb.CoordinatorAPI `json:"coordinators"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, &coordinatorsResponse{
|
c.JSON(http.StatusOK, &coordinatorsResponse{
|
||||||
Coordinators: coordinators,
|
Coordinators: coordinators,
|
||||||
Pagination: pagination,
|
PendingItems: pendingItems,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
"github.com/mitchellh/copystructure"
|
"github.com/mitchellh/copystructure"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -12,18 +11,13 @@ import (
|
|||||||
|
|
||||||
type testCoordinatorsResponse struct {
|
type testCoordinatorsResponse struct {
|
||||||
Coordinators []historydb.CoordinatorAPI `json:"coordinators"`
|
Coordinators []historydb.CoordinatorAPI `json:"coordinators"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testCoordinatorsResponse) GetPagination() *db.Pagination {
|
func (t testCoordinatorsResponse) GetPending() (pendingItems, lastItemID uint64) {
|
||||||
if t.Coordinators[0].ItemID < t.Coordinators[len(t.Coordinators)-1].ItemID {
|
pendingItems = t.PendingItems
|
||||||
t.Pagination.FirstReturnedItem = t.Coordinators[0].ItemID
|
lastItemID = t.Coordinators[len(t.Coordinators)-1].ItemID
|
||||||
t.Pagination.LastReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID
|
return pendingItems, lastItemID
|
||||||
} else {
|
|
||||||
t.Pagination.LastReturnedItem = t.Coordinators[0].ItemID
|
|
||||||
t.Pagination.FirstReturnedItem = t.Coordinators[len(t.Coordinators)-1].ItemID
|
|
||||||
}
|
|
||||||
return t.Pagination
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testCoordinatorsResponse) Len() int { return len(t.Coordinators) }
|
func (t *testCoordinatorsResponse) Len() int { return len(t.Coordinators) }
|
||||||
|
|||||||
11
api/exits.go
11
api/exits.go
@@ -4,7 +4,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,7 +35,7 @@ func (a *API) getExits(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch exits from historyDB
|
// 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,
|
addr, bjj, tokenID, idx, batchNum, onlyPendingWithdraws, fromItem, limit, order,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -46,12 +45,12 @@ func (a *API) getExits(c *gin.Context) {
|
|||||||
|
|
||||||
// Build succesfull response
|
// Build succesfull response
|
||||||
type exitsResponse struct {
|
type exitsResponse struct {
|
||||||
Exits []historydb.ExitAPI `json:"exits"`
|
Exits []historydb.ExitAPI `json:"exits"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, &exitsResponse{
|
c.JSON(http.StatusOK, &exitsResponse{
|
||||||
Exits: exits,
|
Exits: exits,
|
||||||
Pagination: pagination,
|
PendingItems: pendingItems,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hermeznetwork/hermez-node/common"
|
"github.com/hermeznetwork/hermez-node/common"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
"github.com/mitchellh/copystructure"
|
"github.com/mitchellh/copystructure"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -36,19 +35,14 @@ type testExit struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type testExitsResponse struct {
|
type testExitsResponse struct {
|
||||||
Exits []testExit `json:"exits"`
|
Exits []testExit `json:"exits"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testExitsResponse) GetPagination() *db.Pagination {
|
func (t testExitsResponse) GetPending() (pendingItems, lastItemID uint64) {
|
||||||
if t.Exits[0].ItemID < t.Exits[len(t.Exits)-1].ItemID {
|
pendingItems = t.PendingItems
|
||||||
t.Pagination.FirstReturnedItem = t.Exits[0].ItemID
|
lastItemID = t.Exits[len(t.Exits)-1].ItemID
|
||||||
t.Pagination.LastReturnedItem = t.Exits[len(t.Exits)-1].ItemID
|
return pendingItems, lastItemID
|
||||||
} else {
|
|
||||||
t.Pagination.LastReturnedItem = t.Exits[0].ItemID
|
|
||||||
t.Pagination.FirstReturnedItem = t.Exits[len(t.Exits)-1].ItemID
|
|
||||||
}
|
|
||||||
return t.Pagination
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testExitsResponse) Len() int {
|
func (t *testExitsResponse) Len() int {
|
||||||
|
|||||||
85
api/slots.go
85
api/slots.go
@@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/hermeznetwork/hermez-node/common"
|
"github.com/hermeznetwork/hermez-node/common"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,18 +48,6 @@ func (a *API) isOpenAuction(currentBlock, slotNum int64, auctionVars common.Auct
|
|||||||
return false
|
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 {
|
func (a *API) newSlotAPI(slotNum, currentBlockNum int64, bid *historydb.BidAPI, auctionVars *common.AuctionVariables) SlotAPI {
|
||||||
firstBlock, lastBlock := a.getFirstLastBlock(slotNum)
|
firstBlock, lastBlock := a.getFirstLastBlock(slotNum)
|
||||||
openAuction := a.isOpenAuction(currentBlockNum, slotNum, *auctionVars)
|
openAuction := a.isOpenAuction(currentBlockNum, slotNum, *auctionVars)
|
||||||
@@ -141,34 +128,35 @@ func (a *API) getSlot(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, slot)
|
c.JSON(http.StatusOK, slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLimits(minSlotNum, maxSlotNum *int64, fromItem, limit *uint, order string) (int64, int64) {
|
func getLimits(
|
||||||
var minLim, maxLim int64
|
minSlotNum, maxSlotNum int64, fromItem, limit *uint, order string,
|
||||||
if fromItem != nil {
|
) (minLimit, maxLimit int64, pendingItems uint64) {
|
||||||
if order == historydb.OrderAsc {
|
if order == historydb.OrderAsc {
|
||||||
if int64(*fromItem) > *minSlotNum {
|
if fromItem != nil && int64(*fromItem) > minSlotNum {
|
||||||
minLim = int64(*fromItem)
|
minLimit = int64(*fromItem)
|
||||||
} else {
|
|
||||||
minLim = *minSlotNum
|
|
||||||
}
|
|
||||||
if (minLim + int64(*limit-1)) < *maxSlotNum {
|
|
||||||
maxLim = minLim + int64(*limit-1)
|
|
||||||
} else {
|
|
||||||
maxLim = *maxSlotNum
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if int64(*fromItem) < *maxSlotNum {
|
minLimit = minSlotNum
|
||||||
maxLim = int64(*fromItem)
|
|
||||||
} else {
|
|
||||||
maxLim = *maxSlotNum
|
|
||||||
}
|
|
||||||
if (maxLim - int64(*limit-1)) < *minSlotNum {
|
|
||||||
minLim = *minSlotNum
|
|
||||||
} else {
|
|
||||||
minLim = maxLim - int64(*limit-1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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) {
|
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
|
// Get bids and pagination according to filters
|
||||||
var slotMinLim, slotMaxLim int64
|
var slotMinLim, slotMaxLim int64
|
||||||
var bids []historydb.BidAPI
|
var bids []historydb.BidAPI
|
||||||
var pag *db.Pagination
|
var pendingItems uint64
|
||||||
totalItems := uint64(0)
|
|
||||||
if wonByEthereumAddress == nil {
|
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
|
// Get best bids in range maxSlotNum - minSlotNum
|
||||||
bids, _, err = a.h.GetBestBidsAPI(&slotMinLim, &slotMaxLim, wonByEthereumAddress, nil, order)
|
bids, _, err = a.h.GetBestBidsAPI(&slotMinLim, &slotMaxLim, wonByEthereumAddress, nil, order)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
retSQLErr(err, c)
|
retSQLErr(err, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
totalItems = uint64(*maxSlotNum) - uint64(*minSlotNum) + 1
|
|
||||||
} else {
|
} else {
|
||||||
slotMinLim, slotMaxLim = getLimitsWithAddr(minSlotNum, maxSlotNum, fromItem, limit, order)
|
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 {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
retSQLErr(err, c)
|
retSQLErr(err, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(bids) > 0 {
|
|
||||||
totalItems = uint64(pag.TotalItems)
|
|
||||||
*maxSlotNum = int64(pag.LastItem)
|
|
||||||
*minSlotNum = int64(pag.FirstItem)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the slot information with previous bids
|
// Build the slot information with previous bids
|
||||||
@@ -336,11 +317,11 @@ func (a *API) getSlots(c *gin.Context) {
|
|||||||
|
|
||||||
// Build succesfull response
|
// Build succesfull response
|
||||||
type slotsResponse struct {
|
type slotsResponse struct {
|
||||||
Slots []SlotAPI `json:"slots"`
|
Slots []SlotAPI `json:"slots"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, &slotsResponse{
|
c.JSON(http.StatusOK, &slotsResponse{
|
||||||
Slots: slots,
|
Slots: slots,
|
||||||
Pagination: getPagination(totalItems, minSlotNum, maxSlotNum),
|
PendingItems: pendingItems,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hermeznetwork/hermez-node/common"
|
"github.com/hermeznetwork/hermez-node/common"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
"github.com/mitchellh/copystructure"
|
"github.com/mitchellh/copystructure"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testSlot struct {
|
type testSlot struct {
|
||||||
ItemID int `json:"itemId"`
|
ItemID uint64 `json:"itemId"`
|
||||||
SlotNum int64 `json:"slotNum"`
|
SlotNum int64 `json:"slotNum"`
|
||||||
FirstBlock int64 `json:"firstBlock"`
|
FirstBlock int64 `json:"firstBlock"`
|
||||||
LastBlock int64 `json:"lastBlock"`
|
LastBlock int64 `json:"lastBlock"`
|
||||||
@@ -22,19 +21,14 @@ type testSlot struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type testSlotsResponse struct {
|
type testSlotsResponse struct {
|
||||||
Slots []testSlot `json:"slots"`
|
Slots []testSlot `json:"slots"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testSlotsResponse) GetPagination() *db.Pagination {
|
func (t testSlotsResponse) GetPending() (pendingItems, lastItemID uint64) {
|
||||||
if t.Slots[0].ItemID < t.Slots[len(t.Slots)-1].ItemID {
|
pendingItems = t.PendingItems
|
||||||
t.Pagination.FirstReturnedItem = uint64(t.Slots[0].ItemID)
|
lastItemID = t.Slots[len(t.Slots)-1].ItemID
|
||||||
t.Pagination.LastReturnedItem = uint64(t.Slots[len(t.Slots)-1].ItemID)
|
return pendingItems, lastItemID
|
||||||
} 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) Len() int {
|
func (t testSlotsResponse) Len() int {
|
||||||
|
|||||||
@@ -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.
|
* `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.
|
* `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
|
#### Reorgs and safetyness
|
||||||
|
|
||||||
@@ -1739,11 +1741,11 @@ components:
|
|||||||
description: List of history transactions.
|
description: List of history transactions.
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/HistoryTransaction'
|
$ref: '#/components/schemas/HistoryTransaction'
|
||||||
pagination:
|
pendingItems:
|
||||||
$ref: '#/components/schemas/PaginationInfo'
|
$ref: '#/components/schemas/PendingItems'
|
||||||
required:
|
required:
|
||||||
- transactions
|
- transactions
|
||||||
- pagination
|
- pendingItems
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
EthBlockNum:
|
EthBlockNum:
|
||||||
type: integer
|
type: integer
|
||||||
@@ -1877,12 +1879,12 @@ components:
|
|||||||
description: List of batches.
|
description: List of batches.
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Batch'
|
$ref: '#/components/schemas/Batch'
|
||||||
pagination:
|
pendingItems:
|
||||||
$ref: '#/components/schemas/PaginationInfo'
|
$ref: '#/components/schemas/PendingItems'
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
required:
|
required:
|
||||||
- batches
|
- batches
|
||||||
- pagination
|
- pendingItems
|
||||||
Coordinator:
|
Coordinator:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -1914,12 +1916,12 @@ components:
|
|||||||
description: List of coordinators.
|
description: List of coordinators.
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Coordinator'
|
$ref: '#/components/schemas/Coordinator'
|
||||||
pagination:
|
pendingItems:
|
||||||
$ref: '#/components/schemas/PaginationInfo'
|
$ref: '#/components/schemas/PendingItems'
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
required:
|
required:
|
||||||
- coordinators
|
- coordinators
|
||||||
- pagination
|
- pendingItems
|
||||||
Bid:
|
Bid:
|
||||||
type: object
|
type: object
|
||||||
description: Tokens placed in an auction by a coordinator to gain the right to forge batches during a specific slot.
|
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.
|
description: List of bids.
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Bid'
|
$ref: '#/components/schemas/Bid'
|
||||||
pagination:
|
pendingItems:
|
||||||
$ref: '#/components/schemas/PaginationInfo'
|
$ref: '#/components/schemas/PendingItems'
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
require:
|
require:
|
||||||
- bids
|
- bids
|
||||||
- pagination
|
- pendingItems
|
||||||
RecommendedFee:
|
RecommendedFee:
|
||||||
type: object
|
type: object
|
||||||
description: Fee that the coordinator recommends per transaction in USD.
|
description: Fee that the coordinator recommends per transaction in USD.
|
||||||
@@ -2042,8 +2044,8 @@ components:
|
|||||||
description: List of tokens.
|
description: List of tokens.
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Token'
|
$ref: '#/components/schemas/Token'
|
||||||
pagination:
|
pendingItems:
|
||||||
$ref: '#/components/schemas/PaginationInfo'
|
$ref: '#/components/schemas/PendingItems'
|
||||||
Exit:
|
Exit:
|
||||||
type: object
|
type: object
|
||||||
description: Exit tree leaf. It Contains the necessary information to perform a withdrawal.
|
description: Exit tree leaf. It Contains the necessary information to perform a withdrawal.
|
||||||
@@ -2135,11 +2137,11 @@ components:
|
|||||||
description: List of exits.
|
description: List of exits.
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Exit'
|
$ref: '#/components/schemas/Exit'
|
||||||
pagination:
|
pendingItems:
|
||||||
$ref: '#/components/schemas/PaginationInfo'
|
$ref: '#/components/schemas/PendingItems'
|
||||||
required:
|
required:
|
||||||
- exits
|
- exits
|
||||||
- pagination
|
- pendingItems
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
Account:
|
Account:
|
||||||
type: object
|
type: object
|
||||||
@@ -2176,12 +2178,12 @@ components:
|
|||||||
description: List of accounts.
|
description: List of accounts.
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/Account'
|
$ref: '#/components/schemas/Account'
|
||||||
pagination:
|
pendingItems:
|
||||||
$ref: '#/components/schemas/PaginationInfo'
|
$ref: '#/components/schemas/PendingItems'
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
required:
|
required:
|
||||||
- accounts
|
- accounts
|
||||||
- pagination
|
- pendingItems
|
||||||
Slot:
|
Slot:
|
||||||
type: object
|
type: object
|
||||||
description: Slot information.
|
description: Slot information.
|
||||||
@@ -2255,12 +2257,12 @@ components:
|
|||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/Slot'
|
- $ref: '#/components/schemas/Slot'
|
||||||
- description: Last synchronized Etherum block.
|
- description: Last synchronized Etherum block.
|
||||||
pagination:
|
pendingItems:
|
||||||
$ref: '#/components/schemas/PaginationInfo'
|
$ref: '#/components/schemas/PendingItems'
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
require:
|
require:
|
||||||
- slots
|
- slots
|
||||||
- pagination
|
- pendingItems
|
||||||
NextForger:
|
NextForger:
|
||||||
type: object
|
type: object
|
||||||
description: Coordinator information along with the scheduled forging period
|
description: Coordinator information along with the scheduled forging period
|
||||||
@@ -2289,8 +2291,8 @@ components:
|
|||||||
description: List of next coordinators to forge.
|
description: List of next coordinators to forge.
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/NextForger'
|
$ref: '#/components/schemas/NextForger'
|
||||||
pagination:
|
pendingItems:
|
||||||
$ref: '#/components/schemas/PaginationInfo'
|
$ref: '#/components/schemas/PendingItems'
|
||||||
State:
|
State:
|
||||||
type: object
|
type: object
|
||||||
description: Gobal variables of the network
|
description: Gobal variables of the network
|
||||||
@@ -2517,22 +2519,10 @@ components:
|
|||||||
- auction
|
- auction
|
||||||
- withdrawalDelayer
|
- withdrawalDelayer
|
||||||
- recomendedFee
|
- recomendedFee
|
||||||
PaginationInfo:
|
PendingItems:
|
||||||
type: object
|
type: integer
|
||||||
description: Give pagination information
|
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.
|
||||||
properties:
|
example: 15
|
||||||
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
|
|
||||||
Config:
|
Config:
|
||||||
type: object
|
type: object
|
||||||
description: Configuration parameters of the different smart contracts that power the Hermez network.
|
description: Configuration parameters of the different smart contracts that power the Hermez network.
|
||||||
|
|||||||
11
api/token.go
11
api/token.go
@@ -6,7 +6,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/hermeznetwork/hermez-node/common"
|
"github.com/hermeznetwork/hermez-node/common"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ func (a *API) getTokens(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Fetch exits from historyDB
|
// Fetch exits from historyDB
|
||||||
tokens, pagination, err := a.h.GetTokens(
|
tokens, pendingItems, err := a.h.GetTokens(
|
||||||
tokenIDs, symbols, name, fromItem, limit, order,
|
tokenIDs, symbols, name, fromItem, limit, order,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -56,11 +55,11 @@ func (a *API) getTokens(c *gin.Context) {
|
|||||||
|
|
||||||
// Build succesfull response
|
// Build succesfull response
|
||||||
type tokensResponse struct {
|
type tokensResponse struct {
|
||||||
Tokens []historydb.TokenWithUSD `json:"tokens"`
|
Tokens []historydb.TokenWithUSD `json:"tokens"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, &tokensResponse{
|
c.JSON(http.StatusOK, &tokensResponse{
|
||||||
Tokens: tokens,
|
Tokens: tokens,
|
||||||
Pagination: pagination,
|
PendingItems: pendingItems,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
"github.com/mitchellh/copystructure"
|
"github.com/mitchellh/copystructure"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -13,19 +12,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type testTokensResponse struct {
|
type testTokensResponse struct {
|
||||||
Tokens []historydb.TokenWithUSD `json:"tokens"`
|
Tokens []historydb.TokenWithUSD `json:"tokens"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testTokensResponse) GetPagination() *db.Pagination {
|
func (t testTokensResponse) GetPending() (pendingItems, lastItemID uint64) {
|
||||||
if t.Tokens[0].ItemID < t.Tokens[len(t.Tokens)-1].ItemID {
|
pendingItems = t.PendingItems
|
||||||
t.Pagination.FirstReturnedItem = t.Tokens[0].ItemID
|
lastItemID = t.Tokens[len(t.Tokens)-1].ItemID
|
||||||
t.Pagination.LastReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID
|
return pendingItems, lastItemID
|
||||||
} else {
|
|
||||||
t.Pagination.LastReturnedItem = t.Tokens[0].ItemID
|
|
||||||
t.Pagination.FirstReturnedItem = t.Tokens[len(t.Tokens)-1].ItemID
|
|
||||||
}
|
|
||||||
return t.Pagination
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testTokensResponse) Len() int {
|
func (t *testTokensResponse) Len() int {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,7 +34,7 @@ func (a *API) getHistoryTxs(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch txs from historyDB
|
// 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,
|
addr, bjj, tokenID, idx, batchNum, txType, fromItem, limit, order,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -45,12 +44,12 @@ func (a *API) getHistoryTxs(c *gin.Context) {
|
|||||||
|
|
||||||
// Build succesfull response
|
// Build succesfull response
|
||||||
type txsResponse struct {
|
type txsResponse struct {
|
||||||
Txs []historydb.TxAPI `json:"transactions"`
|
Txs []historydb.TxAPI `json:"transactions"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, &txsResponse{
|
c.JSON(http.StatusOK, &txsResponse{
|
||||||
Txs: txs,
|
Txs: txs,
|
||||||
Pagination: pagination,
|
PendingItems: pendingItems,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
"github.com/hermeznetwork/hermez-node/apitypes"
|
"github.com/hermeznetwork/hermez-node/apitypes"
|
||||||
"github.com/hermeznetwork/hermez-node/common"
|
"github.com/hermeznetwork/hermez-node/common"
|
||||||
"github.com/hermeznetwork/hermez-node/db"
|
|
||||||
"github.com/hermeznetwork/hermez-node/db/historydb"
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
||||||
"github.com/hermeznetwork/hermez-node/test"
|
"github.com/hermeznetwork/hermez-node/test"
|
||||||
"github.com/mitchellh/copystructure"
|
"github.com/mitchellh/copystructure"
|
||||||
@@ -53,19 +52,14 @@ type testTx struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type testTxsResponse struct {
|
type testTxsResponse struct {
|
||||||
Txs []testTx `json:"transactions"`
|
Txs []testTx `json:"transactions"`
|
||||||
Pagination *db.Pagination `json:"pagination"`
|
PendingItems uint64 `json:"pendingItems"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testTxsResponse) GetPagination() *db.Pagination {
|
func (t testTxsResponse) GetPending() (pendingItems, lastItemID uint64) {
|
||||||
if t.Txs[0].ItemID < t.Txs[len(t.Txs)-1].ItemID {
|
pendingItems = t.PendingItems
|
||||||
t.Pagination.FirstReturnedItem = t.Txs[0].ItemID
|
lastItemID = t.Txs[len(t.Txs)-1].ItemID
|
||||||
t.Pagination.LastReturnedItem = t.Txs[len(t.Txs)-1].ItemID
|
return pendingItems, lastItemID
|
||||||
} else {
|
|
||||||
t.Pagination.LastReturnedItem = t.Txs[0].ItemID
|
|
||||||
t.Pagination.FirstReturnedItem = t.Txs[len(t.Txs)-1].ItemID
|
|
||||||
}
|
|
||||||
return t.Pagination
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testTxsResponse) Len() int {
|
func (t testTxsResponse) Len() int {
|
||||||
|
|||||||
@@ -181,12 +181,11 @@ func (hdb *HistoryDB) GetBatchesAPI(
|
|||||||
minBatchNum, maxBatchNum, slotNum *uint,
|
minBatchNum, maxBatchNum, slotNum *uint,
|
||||||
forgerAddr *ethCommon.Address,
|
forgerAddr *ethCommon.Address,
|
||||||
fromItem, limit *uint, order string,
|
fromItem, limit *uint, order string,
|
||||||
) ([]BatchAPI, *db.Pagination, error) {
|
) ([]BatchAPI, uint64, error) {
|
||||||
var query string
|
var query string
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
queryStr := `SELECT batch.*, block.timestamp, block.hash,
|
queryStr := `SELECT batch.*, block.timestamp, block.hash,
|
||||||
count(*) OVER() AS total_items, MIN(batch.item_id) OVER() AS first_item,
|
count(*) OVER() AS total_items
|
||||||
MAX(batch.item_id) OVER() AS last_item
|
|
||||||
FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num `
|
FROM batch INNER JOIN block ON batch.eth_block_num = block.eth_block_num `
|
||||||
// Apply filters
|
// Apply filters
|
||||||
nextIsAnd := false
|
nextIsAnd := false
|
||||||
@@ -259,17 +258,13 @@ func (hdb *HistoryDB) GetBatchesAPI(
|
|||||||
// log.Debug(query)
|
// log.Debug(query)
|
||||||
batchPtrs := []*BatchAPI{}
|
batchPtrs := []*BatchAPI{}
|
||||||
if err := meddler.QueryAll(hdb.db, &batchPtrs, query, args...); err != nil {
|
if err := meddler.QueryAll(hdb.db, &batchPtrs, query, args...); err != nil {
|
||||||
return nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
batches := db.SlicePtrsToSlice(batchPtrs).([]BatchAPI)
|
batches := db.SlicePtrsToSlice(batchPtrs).([]BatchAPI)
|
||||||
if len(batches) == 0 {
|
if len(batches) == 0 {
|
||||||
return nil, nil, sql.ErrNoRows
|
return nil, 0, sql.ErrNoRows
|
||||||
}
|
}
|
||||||
return batches, &db.Pagination{
|
return batches, batches[0].TotalItems - uint64(len(batches)), nil
|
||||||
TotalItems: batches[0].TotalItems,
|
|
||||||
FirstItem: batches[0].FirstItem,
|
|
||||||
LastItem: batches[0].LastItem,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllBatches retrieve all batches from the DB
|
// 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
|
// 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 query string
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
queryStr := `SELECT b.*, block.timestamp, coordinator.forger_addr, coordinator.url,
|
queryStr := `SELECT b.*, block.timestamp, coordinator.forger_addr, coordinator.url,
|
||||||
COUNT(*) OVER() AS total_items, MIN(b.slot_num) OVER() AS first_item,
|
COUNT(*) OVER() AS total_items FROM (
|
||||||
MAX(b.slot_num) OVER() AS last_item FROM (
|
|
||||||
SELECT slot_num, MAX(item_id) as maxitem
|
SELECT slot_num, MAX(item_id) as maxitem
|
||||||
FROM bid GROUP BY slot_num
|
FROM bid GROUP BY slot_num
|
||||||
)
|
)
|
||||||
@@ -399,28 +397,26 @@ func (hdb *HistoryDB) GetBestBidsAPI(minSlotNum, maxSlotNum *int64, bidderAddr *
|
|||||||
query = hdb.db.Rebind(queryStr)
|
query = hdb.db.Rebind(queryStr)
|
||||||
bidPtrs := []*BidAPI{}
|
bidPtrs := []*BidAPI{}
|
||||||
if err := meddler.QueryAll(hdb.db, &bidPtrs, query, args...); err != nil {
|
if err := meddler.QueryAll(hdb.db, &bidPtrs, query, args...); err != nil {
|
||||||
return nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
// log.Debug(query)
|
// log.Debug(query)
|
||||||
bids := db.SlicePtrsToSlice(bidPtrs).([]BidAPI)
|
bids := db.SlicePtrsToSlice(bidPtrs).([]BidAPI)
|
||||||
if len(bids) == 0 {
|
if len(bids) == 0 {
|
||||||
return nil, nil, sql.ErrNoRows
|
return nil, 0, sql.ErrNoRows
|
||||||
}
|
}
|
||||||
return bids, &db.Pagination{
|
return bids, bids[0].TotalItems - uint64(len(bids)), nil
|
||||||
TotalItems: bids[0].TotalItems,
|
|
||||||
FirstItem: bids[0].FirstItem,
|
|
||||||
LastItem: bids[0].LastItem,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBidsAPI return the bids applying the given filters
|
// 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 query string
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
queryStr := `SELECT bid.*, block.timestamp, coordinator.forger_addr, coordinator.url,
|
queryStr := `SELECT bid.*, block.timestamp, coordinator.forger_addr, coordinator.url,
|
||||||
COUNT(*) OVER() AS total_items, MIN(bid.item_id) OVER() AS first_item,
|
COUNT(*) OVER() AS total_items
|
||||||
MAX(bid.item_id) OVER() AS last_item FROM bid
|
FROM bid INNER JOIN block ON bid.eth_block_num = block.eth_block_num
|
||||||
INNER JOIN block ON bid.eth_block_num = block.eth_block_num
|
|
||||||
INNER JOIN coordinator ON bid.bidder_addr = coordinator.bidder_addr `
|
INNER JOIN coordinator ON bid.bidder_addr = coordinator.bidder_addr `
|
||||||
// Apply filters
|
// Apply filters
|
||||||
nextIsAnd := false
|
nextIsAnd := false
|
||||||
@@ -469,21 +465,17 @@ func (hdb *HistoryDB) GetBidsAPI(slotNum *int64, forgerAddr *ethCommon.Address,
|
|||||||
queryStr += fmt.Sprintf("LIMIT %d;", *limit)
|
queryStr += fmt.Sprintf("LIMIT %d;", *limit)
|
||||||
query, argsQ, err := sqlx.In(queryStr, args...)
|
query, argsQ, err := sqlx.In(queryStr, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
query = hdb.db.Rebind(query)
|
query = hdb.db.Rebind(query)
|
||||||
bids := []*BidAPI{}
|
bids := []*BidAPI{}
|
||||||
if err := meddler.QueryAll(hdb.db, &bids, query, argsQ...); err != nil {
|
if err := meddler.QueryAll(hdb.db, &bids, query, argsQ...); err != nil {
|
||||||
return nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
if len(bids) == 0 {
|
if len(bids) == 0 {
|
||||||
return nil, nil, sql.ErrNoRows
|
return nil, 0, sql.ErrNoRows
|
||||||
}
|
}
|
||||||
return db.SlicePtrsToSlice(bids).([]BidAPI), &db.Pagination{
|
return db.SlicePtrsToSlice(bids).([]BidAPI), bids[0].TotalItems - uint64(len(bids)), nil
|
||||||
TotalItems: bids[0].TotalItems,
|
|
||||||
FirstItem: bids[0].FirstItem,
|
|
||||||
LastItem: bids[0].LastItem,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddCoordinators insert Coordinators into the DB
|
// 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
|
// 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 query string
|
||||||
var args []interface{}
|
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
|
// Apply filters
|
||||||
nextIsAnd := false
|
nextIsAnd := false
|
||||||
if len(ids) > 0 {
|
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)
|
queryStr += fmt.Sprintf("LIMIT %d;", *limit)
|
||||||
query, argsQ, err := sqlx.In(queryStr, args...)
|
query, argsQ, err := sqlx.In(queryStr, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
query = hdb.db.Rebind(query)
|
query = hdb.db.Rebind(query)
|
||||||
tokens := []*TokenWithUSD{}
|
tokens := []*TokenWithUSD{}
|
||||||
if err := meddler.QueryAll(hdb.db, &tokens, query, argsQ...); err != nil {
|
if err := meddler.QueryAll(hdb.db, &tokens, query, argsQ...); err != nil {
|
||||||
return nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
if len(tokens) == 0 {
|
if len(tokens) == 0 {
|
||||||
return nil, nil, sql.ErrNoRows
|
return nil, 0, sql.ErrNoRows
|
||||||
}
|
}
|
||||||
return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), &db.Pagination{
|
return db.SlicePtrsToSlice(tokens).([]TokenWithUSD), uint64(len(tokens)) - tokens[0].TotalItems, nil
|
||||||
TotalItems: tokens[0].TotalItems,
|
|
||||||
FirstItem: tokens[0].FirstItem,
|
|
||||||
LastItem: tokens[0].LastItem,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTokenSymbols returns all the token symbols from the DB
|
// GetTokenSymbols returns all the token symbols from the DB
|
||||||
@@ -813,9 +804,9 @@ func (hdb *HistoryDB) GetHistoryTxs(
|
|||||||
ethAddr *ethCommon.Address, bjj *babyjub.PublicKey,
|
ethAddr *ethCommon.Address, bjj *babyjub.PublicKey,
|
||||||
tokenID *common.TokenID, idx *common.Idx, batchNum *uint, txType *common.TxType,
|
tokenID *common.TokenID, idx *common.Idx, batchNum *uint, txType *common.TxType,
|
||||||
fromItem, limit *uint, order string,
|
fromItem, limit *uint, order string,
|
||||||
) ([]TxAPI, *db.Pagination, error) {
|
) ([]TxAPI, uint64, error) {
|
||||||
if ethAddr != nil && bjj != nil {
|
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 query string
|
||||||
var args []interface{}
|
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,
|
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.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.eth_addr, token.name, token.symbol, token.decimals, token.usd,
|
||||||
token.usd_update, block.timestamp, count(*) OVER() AS total_items,
|
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
|
|
||||||
FROM tx INNER JOIN token ON tx.token_id = token.token_id
|
FROM tx INNER JOIN token ON tx.token_id = token.token_id
|
||||||
INNER JOIN block ON tx.eth_block_num = block.eth_block_num `
|
INNER JOIN block ON tx.eth_block_num = block.eth_block_num `
|
||||||
// Apply filters
|
// Apply filters
|
||||||
@@ -924,17 +914,13 @@ func (hdb *HistoryDB) GetHistoryTxs(
|
|||||||
// log.Debug(query)
|
// log.Debug(query)
|
||||||
txsPtrs := []*TxAPI{}
|
txsPtrs := []*TxAPI{}
|
||||||
if err := meddler.QueryAll(hdb.db, &txsPtrs, query, args...); err != nil {
|
if err := meddler.QueryAll(hdb.db, &txsPtrs, query, args...); err != nil {
|
||||||
return nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
txs := db.SlicePtrsToSlice(txsPtrs).([]TxAPI)
|
txs := db.SlicePtrsToSlice(txsPtrs).([]TxAPI)
|
||||||
if len(txs) == 0 {
|
if len(txs) == 0 {
|
||||||
return nil, nil, sql.ErrNoRows
|
return nil, 0, sql.ErrNoRows
|
||||||
}
|
}
|
||||||
return txs, &db.Pagination{
|
return txs, txs[0].TotalItems - uint64(len(txs)), nil
|
||||||
TotalItems: txs[0].TotalItems,
|
|
||||||
FirstItem: txs[0].FirstItem,
|
|
||||||
LastItem: txs[0].LastItem,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllExits returns all exit from the DB
|
// GetAllExits returns all exit from the DB
|
||||||
@@ -972,9 +958,9 @@ func (hdb *HistoryDB) GetExitsAPI(
|
|||||||
ethAddr *ethCommon.Address, bjj *babyjub.PublicKey, tokenID *common.TokenID,
|
ethAddr *ethCommon.Address, bjj *babyjub.PublicKey, tokenID *common.TokenID,
|
||||||
idx *common.Idx, batchNum *uint, onlyPendingWithdraws *bool,
|
idx *common.Idx, batchNum *uint, onlyPendingWithdraws *bool,
|
||||||
fromItem, limit *uint, order string,
|
fromItem, limit *uint, order string,
|
||||||
) ([]ExitAPI, *db.Pagination, error) {
|
) ([]ExitAPI, uint64, error) {
|
||||||
if ethAddr != nil && bjj != nil {
|
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 query string
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
@@ -984,8 +970,7 @@ func (hdb *HistoryDB) GetExitsAPI(
|
|||||||
exit_tree.delayed_withdraw_request, exit_tree.delayed_withdrawn,
|
exit_tree.delayed_withdraw_request, exit_tree.delayed_withdrawn,
|
||||||
token.token_id, token.item_id AS token_item_id,
|
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.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,
|
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
|
|
||||||
FROM exit_tree INNER JOIN account ON exit_tree.account_idx = account.idx
|
FROM exit_tree INNER JOIN account ON exit_tree.account_idx = account.idx
|
||||||
INNER JOIN token ON account.token_id = token.token_id `
|
INNER JOIN token ON account.token_id = token.token_id `
|
||||||
// Apply filters
|
// Apply filters
|
||||||
@@ -1071,16 +1056,12 @@ func (hdb *HistoryDB) GetExitsAPI(
|
|||||||
// log.Debug(query)
|
// log.Debug(query)
|
||||||
exits := []*ExitAPI{}
|
exits := []*ExitAPI{}
|
||||||
if err := meddler.QueryAll(hdb.db, &exits, query, args...); err != nil {
|
if err := meddler.QueryAll(hdb.db, &exits, query, args...); err != nil {
|
||||||
return nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
if len(exits) == 0 {
|
if len(exits) == 0 {
|
||||||
return nil, nil, sql.ErrNoRows
|
return nil, 0, sql.ErrNoRows
|
||||||
}
|
}
|
||||||
return db.SlicePtrsToSlice(exits).([]ExitAPI), &db.Pagination{
|
return db.SlicePtrsToSlice(exits).([]ExitAPI), exits[0].TotalItems - uint64(len(exits)), nil
|
||||||
TotalItems: exits[0].TotalItems,
|
|
||||||
FirstItem: exits[0].FirstItem,
|
|
||||||
LastItem: exits[0].LastItem,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// // GetTx returns a tx from the DB
|
// // 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
|
// 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 query string
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
queryStr := `SELECT coordinator.*,
|
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 `
|
FROM coordinator `
|
||||||
// Apply filters
|
// Apply filters
|
||||||
if fromItem != nil {
|
if fromItem != nil {
|
||||||
@@ -1365,16 +1346,13 @@ func (hdb *HistoryDB) GetCoordinatorsAPI(fromItem, limit *uint, order string) ([
|
|||||||
|
|
||||||
coordinators := []*CoordinatorAPI{}
|
coordinators := []*CoordinatorAPI{}
|
||||||
if err := meddler.QueryAll(hdb.db, &coordinators, query, args...); err != nil {
|
if err := meddler.QueryAll(hdb.db, &coordinators, query, args...); err != nil {
|
||||||
return nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
if len(coordinators) == 0 {
|
if len(coordinators) == 0 {
|
||||||
return nil, nil, sql.ErrNoRows
|
return nil, 0, sql.ErrNoRows
|
||||||
}
|
}
|
||||||
return db.SlicePtrsToSlice(coordinators).([]CoordinatorAPI), &db.Pagination{
|
return db.SlicePtrsToSlice(coordinators).([]CoordinatorAPI),
|
||||||
TotalItems: coordinators[0].TotalItems,
|
coordinators[0].TotalItems - uint64(len(coordinators)), nil
|
||||||
FirstItem: coordinators[0].FirstItem,
|
|
||||||
LastItem: coordinators[0].LastItem,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddAuctionVars insert auction vars into the DB
|
// 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
|
// GetAccountAPI returns an account by its index
|
||||||
func (hdb *HistoryDB) GetAccountAPI(idx common.Idx) (*AccountAPI, error) {
|
func (hdb *HistoryDB) GetAccountAPI(idx common.Idx) (*AccountAPI, error) {
|
||||||
account := &AccountAPI{}
|
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.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
|
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)
|
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
|
// 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 {
|
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 query string
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
queryStr := `SELECT account.item_id, hez_idx(account.idx, token.symbol) as idx, account.batch_num, account.bjj, account.eth_addr,
|
queryStr := `SELECT account.item_id, hez_idx(account.idx, token.symbol) as idx, account.batch_num,
|
||||||
token.token_id, token.item_id AS token_item_id, token.eth_block_num AS token_block,
|
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,
|
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 `
|
FROM account INNER JOIN token ON account.token_id = token.token_id `
|
||||||
// Apply filters
|
// Apply filters
|
||||||
nextIsAnd := false
|
nextIsAnd := false
|
||||||
@@ -1464,21 +1446,18 @@ func (hdb *HistoryDB) GetAccountsAPI(tokenIDs []common.TokenID, ethAddr *ethComm
|
|||||||
queryStr += fmt.Sprintf("LIMIT %d;", *limit)
|
queryStr += fmt.Sprintf("LIMIT %d;", *limit)
|
||||||
query, argsQ, err := sqlx.In(queryStr, args...)
|
query, argsQ, err := sqlx.In(queryStr, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
query = hdb.db.Rebind(query)
|
query = hdb.db.Rebind(query)
|
||||||
|
|
||||||
accounts := []*AccountAPI{}
|
accounts := []*AccountAPI{}
|
||||||
if err := meddler.QueryAll(hdb.db, &accounts, query, argsQ...); err != nil {
|
if err := meddler.QueryAll(hdb.db, &accounts, query, argsQ...); err != nil {
|
||||||
return nil, nil, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
if len(accounts) == 0 {
|
if len(accounts) == 0 {
|
||||||
return nil, nil, sql.ErrNoRows
|
return nil, 0, sql.ErrNoRows
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.SlicePtrsToSlice(accounts).([]AccountAPI), &db.Pagination{
|
return db.SlicePtrsToSlice(accounts).([]AccountAPI),
|
||||||
TotalItems: accounts[0].TotalItems,
|
accounts[0].TotalItems - uint64(len(accounts)), nil
|
||||||
FirstItem: accounts[0].FirstItem,
|
|
||||||
LastItem: accounts[0].LastItem,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
15
db/utils.go
15
db/utils.go
@@ -179,21 +179,6 @@ func SlicePtrsToSlice(slice interface{}) interface{} {
|
|||||||
return res.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
|
// Rollback an sql transaction, and log the error if it's not nil
|
||||||
func Rollback(txn *sql.Tx) {
|
func Rollback(txn *sql.Tx) {
|
||||||
err := txn.Rollback()
|
err := txn.Rollback()
|
||||||
|
|||||||
Reference in New Issue
Block a user