add (feat) pagination helpers and token endpoint

- see docs for pagination api usage
- added control for syncer/api goroutines
This commit is contained in:
Mohamed Sohail 2022-05-19 16:24:26 +03:00
parent 2548e68c36
commit 37538c68ff
Signed by: kamikazechaser
GPG Key ID: 7DD45520C01CD85D
7 changed files with 153 additions and 19 deletions

View File

@ -33,6 +33,12 @@ type config struct {
TokenRegistry string `koanf:"index"` TokenRegistry string `koanf:"index"`
BalanceResolver string `koanf:"balances_resolver"` BalanceResolver string `koanf:"balances_resolver"`
} }
Syncer struct {
Enabled bool `koanf:"enabled"`
}
Api struct {
Enabled bool `koan:"enabled"`
}
Syncers map[string]string `koanf:"syncers"` Syncers map[string]string `koanf:"syncers"`
} }

View File

@ -70,20 +70,25 @@ func main() {
log.Fatal().Err(err).Msg("could not bootstrap scheduler") log.Fatal().Err(err).Msg("could not bootstrap scheduler")
} }
processor, mux := bootstrapProcessor(rClient)
if conf.Syncer.Enabled {
go func() { go func() {
if err := scheduler.Run(); err != nil { if err := scheduler.Run(); err != nil {
log.Fatal().Err(err).Msg("could not start scheduler") log.Fatal().Err(err).Msg("could not start scheduler")
} }
}() }()
processor, mux := bootstrapProcessor(rClient)
go func() { go func() {
if err := processor.Run(mux); err != nil { if err := processor.Run(mux); err != nil {
log.Fatal().Err(err).Msg("failed to start job processor") log.Fatal().Err(err).Msg("failed to start job processor")
} }
}() }()
}
server := initHTTPServer() server := initHTTPServer()
if conf.Api.Enabled {
go func() { go func() {
if err := server.Start(conf.Server.Address); err != nil { if err := server.Start(conf.Server.Address); err != nil {
if strings.Contains(err.Error(), "Server closed") { if strings.Contains(err.Error(), "Server closed") {
@ -93,6 +98,7 @@ func main() {
} }
} }
}() }()
}
sigs := make(chan os.Signal, 1) sigs := make(chan os.Signal, 1)
signal.Notify(sigs, unix.SIGTERM, unix.SIGINT, unix.SIGTSTP) signal.Notify(sigs, unix.SIGTERM, unix.SIGINT, unix.SIGTSTP)

View File

@ -16,6 +16,12 @@ index = "0x5A1EB529438D8b3cA943A45a48744f4c73d1f098"
balances_resolver = "0xb9e215B789e9Ec6643Ba4ff7b98EA219F38c6fE5" balances_resolver = "0xb9e215B789e9Ec6643Ba4ff7b98EA219F38c6fE5"
rpc = "http://127.0.0.1:8545" rpc = "http://127.0.0.1:8545"
[syncer]
enabled = true
[api]
enabled = true
[syncers] [syncers]
cache = "@every 20s" cache = "@every 20s"
ussd = "@every 30s" ussd = "@every 30s"

View File

@ -27,5 +27,9 @@ func InitPublicApi(e *echo.Echo, db *pgxpool.Pool, batchBalance *batch_balance.B
} }
}) })
// TODO: paginate schema validation
g.GET("/balances/:address", handleBalancesQuery) g.GET("/balances/:address", handleBalancesQuery)
g.GET("/tokens-count", handleTokensCountQuery)
g.GET("/tokens", handleTokenListQuery)
} }

65
internal/public/tokens.go Normal file
View File

@ -0,0 +1,65 @@
package public
import (
"cic-dw/pkg/pagination"
"context"
"net/http"
"github.com/georgysavva/scany/pgxscan"
"github.com/labstack/echo/v4"
)
type tokensRes struct {
Id int `db:"id" json:"id"`
TokenSymbol string `db:"token_symbol" json:"token_symbol"`
TokenName string `db:"token_name" json:"token_name"`
TokenAddress string `db:"token_address" json:"token_addres"`
}
type tokenCountRes struct {
Count int `db:"count" json:"count"`
}
func handleTokenListQuery(c echo.Context) error {
var (
api = c.Get("api").(*api)
pg = pagination.GetPagination(c.QueryParams())
res []tokensRes
q string
)
if pg.Forward {
q = api.q["list-tokens-fwd"]
} else {
q = api.q["list-tokens-bkwd"]
}
rows, err := api.db.Query(context.Background(), q, pg.Cursor, pg.PerPage)
if err != nil {
return err
}
if err := pgxscan.ScanAll(&res, rows); err != nil {
return err
}
return c.JSON(http.StatusOK, res)
}
func handleTokensCountQuery(c echo.Context) error {
var (
api = c.Get("api").(*api)
res tokenCountRes
)
rows, err := api.db.Query(context.Background(), api.q["tokens-count"])
if err != nil {
return err
}
if err := pgxscan.ScanOne(&res, rows); err != nil {
return err
}
return c.JSON(http.StatusOK, res)
}

View File

@ -0,0 +1,34 @@
package pagination
import (
"net/url"
"strconv"
)
type Pagination struct {
PerPage int
Cursor int
Forward bool
}
func GetPagination(q url.Values) Pagination {
var (
pp, _ = strconv.Atoi(q.Get("per_page"))
cursor, _ = strconv.Atoi(q.Get("cursor"))
forward, _ = strconv.ParseBool(q.Get("forward"))
)
if pp > 100 {
pp = 100
}
if !forward && cursor < 1 {
cursor = 1
}
return Pagination{
PerPage: pp,
Cursor: cursor,
Forward: forward,
}
}

View File

@ -4,3 +4,16 @@ SELECT DISTINCT tokens.token_symbol, tokens.token_address FROM transactions
INNER JOIN tokens on transactions.token_address = tokens.token_address INNER JOIN tokens on transactions.token_address = tokens.token_address
WHERE transactions.sender_address = $1 WHERE transactions.sender_address = $1
OR transactions.recipient_address = $1; OR transactions.recipient_address = $1;
-- Bidirectional cursor pagianators
-- name: list-tokens-fwd
SELECT tokens.id, tokens.token_address, tokens.token_name, tokens.token_symbol FROM tokens
WHERE tokens.id > $1 ORDER BY tokens.id ASC LIMIT $2;
-- name: list-tokens-bkwd
SELECT tokens.id, tokens.token_address, tokens.token_name, tokens.token_symbol FROM tokens
WHERE tokens.id < $1 ORDER BY tokens.id ASC LIMIT $2;
-- name: tokens-count
-- Return total record count from individual i= tables/views
SELECT COUNT(*) FROM tokens;