[API] Several fixes related to pagination (#144)
### Summary * On all endpoints, fix the behavior of the `page` parameter (which was previously being ignored). * On `GET /api/v1/vaas`, fix the behavior of the `sortBy` parameter (which wasn't working when `parsedPayload=true`). * For all endpoints, validate the query parameters `page`, `pageNumber` and `sortOrder`. * Return descriptive errors when pagination-related parameters happen to be invalid (`page`, `pageSize`, `sortOrder`). * Validate guardian addresses in query params. ### Testing plan All parameters involved were manually tested.
This commit is contained in:
parent
b033c04a13
commit
dde87acf84
|
@ -51,7 +51,7 @@ type GovernorQuery struct {
|
|||
|
||||
// QueryGovernor create a new GovernorQuery with default pagination values.
|
||||
func QueryGovernor() *GovernorQuery {
|
||||
page := pagination.FirstPage()
|
||||
page := pagination.Default()
|
||||
return &GovernorQuery{Pagination: *page}
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ func (r *Repository) FindGovConfigurations(ctx context.Context, q *GovernorQuery
|
|||
{Key: "chains", Value: "$parsedConfig.chains"},
|
||||
{Key: "tokens", Value: "$parsedConfig.tokens"},
|
||||
}
|
||||
options := options.Find().SetProjection(projection).SetLimit(q.PageSize).SetSkip(q.Offset).SetSort(sort)
|
||||
options := options.Find().SetProjection(projection).SetLimit(q.Limit).SetSkip(q.Skip).SetSort(sort)
|
||||
cur, err := r.collections.governorConfig.Find(ctx, q.toBSON(), options)
|
||||
if err != nil {
|
||||
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
||||
|
@ -148,7 +148,7 @@ func (r *Repository) FindGovernorStatus(ctx context.Context, q *GovernorQuery) (
|
|||
{Key: "nodename", Value: "$parsedStatus.nodename"},
|
||||
{Key: "chains", Value: "$parsedStatus.chains"},
|
||||
}
|
||||
options := options.Find().SetProjection(projection).SetLimit(q.PageSize).SetSkip(q.Offset).SetSort(sort)
|
||||
options := options.Find().SetProjection(projection).SetLimit(q.Limit).SetSkip(q.Skip).SetSort(sort)
|
||||
cur, err := r.collections.governorStatus.Find(ctx, q.toBSON(), options)
|
||||
if err != nil {
|
||||
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
||||
|
@ -202,7 +202,7 @@ type NotionalLimitQuery struct {
|
|||
|
||||
// QueryNotionalLimit create a new NotionalLimitQuery with default pagination values.
|
||||
func QueryNotionalLimit() *NotionalLimitQuery {
|
||||
page := pagination.FirstPage()
|
||||
page := pagination.Default()
|
||||
return &NotionalLimitQuery{Pagination: *page}
|
||||
}
|
||||
|
||||
|
@ -681,7 +681,7 @@ type EnqueuedVaaQuery struct {
|
|||
|
||||
// QueryEnqueuedVaa create a new EnqueuedVaaQuery with default pagination values.
|
||||
func QueryEnqueuedVaa() *EnqueuedVaaQuery {
|
||||
page := pagination.FirstPage()
|
||||
page := pagination.Default()
|
||||
return &EnqueuedVaaQuery{Pagination: *page}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ func NewService(dao *Repository, logger *zap.Logger) *Service {
|
|||
// FindGovernorConfig get a list of governor configurations.
|
||||
func (s *Service) FindGovernorConfig(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*GovConfig], error) {
|
||||
if p == nil {
|
||||
p = pagination.FirstPage()
|
||||
p = pagination.Default()
|
||||
}
|
||||
query := QueryGovernor().SetPagination(p)
|
||||
govConfigs, err := s.repo.FindGovConfigurations(ctx, query)
|
||||
|
@ -43,7 +43,7 @@ func (s *Service) FindGovernorConfigByGuardianAddress(ctx context.Context, guard
|
|||
// FindGovernorStatus get a list of governor status.
|
||||
func (s *Service) FindGovernorStatus(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*GovStatus], error) {
|
||||
if p == nil {
|
||||
p = pagination.FirstPage()
|
||||
p = pagination.Default()
|
||||
}
|
||||
query := QueryGovernor().SetPagination(p)
|
||||
govStatus, err := s.repo.FindGovernorStatus(ctx, query)
|
||||
|
@ -62,7 +62,7 @@ func (s *Service) FindGovernorStatusByGuardianAddress(ctx context.Context, guard
|
|||
// FindNotionalLimit get a notional limit for each chainID.
|
||||
func (s *Service) FindNotionalLimit(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*NotionalLimit], error) {
|
||||
if p == nil {
|
||||
p = pagination.FirstPage()
|
||||
p = pagination.Default()
|
||||
}
|
||||
query := QueryNotionalLimit().SetPagination(p)
|
||||
notionalLimit, err := s.repo.FindNotionalLimit(ctx, query)
|
||||
|
@ -81,7 +81,7 @@ func (s *Service) GetNotionalLimitByChainID(ctx context.Context, p *pagination.P
|
|||
// GetAvailableNotional get a available notional for each chainID.
|
||||
func (s *Service) GetAvailableNotional(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*NotionalAvailable], error) {
|
||||
if p == nil {
|
||||
p = pagination.FirstPage()
|
||||
p = pagination.Default()
|
||||
}
|
||||
query := QueryNotionalLimit().SetPagination(p)
|
||||
notionalAvailability, err := s.repo.GetAvailableNotional(ctx, query)
|
||||
|
@ -108,7 +108,7 @@ func (s *Service) GetMaxNotionalAvailableByChainID(ctx context.Context, p *pagin
|
|||
// GetEnqueueVaas get all the enqueued vaa.
|
||||
func (s *Service) GetEnqueueVass(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*EnqueuedVaas], error) {
|
||||
if p == nil {
|
||||
p = pagination.FirstPage()
|
||||
p = pagination.Default()
|
||||
}
|
||||
query := QueryEnqueuedVaa().SetPagination(p)
|
||||
enqueuedVaaResponse, err := s.repo.GetEnqueueVass(ctx, query)
|
||||
|
@ -119,7 +119,7 @@ func (s *Service) GetEnqueueVass(ctx context.Context, p *pagination.Pagination)
|
|||
// GetEnqueueVassByChainID get enequeued vaa by chainID.
|
||||
func (s *Service) GetEnqueueVassByChainID(ctx context.Context, p *pagination.Pagination, chainID vaa.ChainID) (*response.Response[[]*EnqueuedVaaDetail], error) {
|
||||
if p == nil {
|
||||
p = pagination.FirstPage()
|
||||
p = pagination.Default()
|
||||
}
|
||||
query := QueryEnqueuedVaa().SetPagination(p).SetChain(chainID)
|
||||
enqueuedVaaRecord, err := s.repo.GetEnqueueVassByChainID(ctx, query)
|
||||
|
@ -130,7 +130,7 @@ func (s *Service) GetEnqueueVassByChainID(ctx context.Context, p *pagination.Pag
|
|||
// GetGovernorLimit get governor limit.
|
||||
func (s *Service) GetGovernorLimit(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*GovernorLimit], error) {
|
||||
if p == nil {
|
||||
p = pagination.FirstPage()
|
||||
p = pagination.Default()
|
||||
}
|
||||
query := QueryGovernor().SetPagination(p)
|
||||
governorLimit, err := s.repo.GetGovernorLimit(ctx, query)
|
||||
|
|
|
@ -39,7 +39,7 @@ func (r *Repository) Find(ctx context.Context, q *ObservationQuery) ([]*Observat
|
|||
q = Query()
|
||||
}
|
||||
sort := bson.D{{q.SortBy, q.GetSortInt()}}
|
||||
cur, err := r.collections.observations.Find(ctx, q.toBSON(), options.Find().SetLimit(q.PageSize).SetSkip(q.Offset).SetSort(sort))
|
||||
cur, err := r.collections.observations.Find(ctx, q.toBSON(), options.Find().SetLimit(q.Limit).SetSkip(q.Skip).SetSort(sort))
|
||||
if err != nil {
|
||||
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
||||
r.logger.Error("failed execute Find command to get observations",
|
||||
|
@ -90,7 +90,7 @@ type ObservationQuery struct {
|
|||
|
||||
// Query create a new ObservationQuery with default pagination vaues.
|
||||
func Query() *ObservationQuery {
|
||||
page := pagination.FirstPage()
|
||||
page := pagination.Default()
|
||||
return &ObservationQuery{Pagination: *page}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,14 +47,12 @@ func (r *Repository) Find(ctx context.Context, q *VaaQuery) ([]*VaaDoc, error) {
|
|||
q = Query()
|
||||
}
|
||||
|
||||
sort := bson.D{{q.SortBy, q.GetSortInt()}}
|
||||
|
||||
var err error
|
||||
var cur *mongo.Cursor
|
||||
if q.chainId == vaa.ChainIDPythNet {
|
||||
cur, err = r.collections.vaasPythnet.Find(ctx, q.toBSON(), options.Find().SetLimit(q.PageSize).SetSkip(q.Offset).SetSort(sort))
|
||||
cur, err = r.collections.vaasPythnet.Find(ctx, q.toBSON(), q.findOptions())
|
||||
} else {
|
||||
cur, err = r.collections.vaas.Find(ctx, q.toBSON(), options.Find().SetLimit(q.PageSize).SetSkip(q.Offset).SetSort(sort))
|
||||
cur, err = r.collections.vaas.Find(ctx, q.toBSON(), q.findOptions())
|
||||
}
|
||||
if err != nil {
|
||||
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
||||
|
@ -122,6 +120,11 @@ func (r *Repository) FindVaasWithPayload(
|
|||
// build a query pipeline based on input parameters
|
||||
var pipeline mongo.Pipeline
|
||||
{
|
||||
// specify sorting criteria
|
||||
pipeline = append(pipeline, bson.D{
|
||||
{"$sort", bson.D{bson.E{q.SortBy, q.GetSortInt()}}},
|
||||
})
|
||||
|
||||
// filter by emitterChain
|
||||
if q.chainId != 0 {
|
||||
pipeline = append(pipeline, bson.D{
|
||||
|
@ -168,9 +171,16 @@ func (r *Repository) FindVaasWithPayload(
|
|||
})
|
||||
}
|
||||
|
||||
// skip initial results
|
||||
if q.Pagination.Skip != 0 {
|
||||
pipeline = append(pipeline, bson.D{
|
||||
{"$skip", q.Pagination.Skip},
|
||||
})
|
||||
}
|
||||
|
||||
// limit size of results
|
||||
pipeline = append(pipeline, bson.D{
|
||||
{"$limit", q.Pagination.PageSize},
|
||||
{"$limit", q.Pagination.Limit},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -207,14 +217,12 @@ func (r *Repository) FindVaasWithPayload(
|
|||
|
||||
// GetVaaCount get a count of vaa by chainID.
|
||||
func (r *Repository) GetVaaCount(ctx context.Context, q *VaaQuery) ([]*VaaStats, error) {
|
||||
|
||||
if q == nil {
|
||||
q = Query()
|
||||
}
|
||||
sort := bson.D{{
|
||||
q.SortBy,
|
||||
q.GetSortInt(),
|
||||
}}
|
||||
cur, err := r.collections.vaaCount.Find(ctx, q.toBSON(), options.Find().SetLimit(q.PageSize).SetSkip(q.Offset).SetSort(sort))
|
||||
|
||||
cur, err := r.collections.vaaCount.Find(ctx, q.toBSON(), q.findOptions())
|
||||
if err != nil {
|
||||
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
||||
r.logger.Error("failed execute Find command to get vaaCount",
|
||||
|
@ -244,7 +252,7 @@ type VaaQuery struct {
|
|||
|
||||
// Query create a new VaaQuery with default pagination vaues.
|
||||
func Query() *VaaQuery {
|
||||
page := pagination.FirstPage()
|
||||
page := pagination.Default()
|
||||
return &VaaQuery{Pagination: *page}
|
||||
}
|
||||
|
||||
|
@ -299,3 +307,14 @@ func (q *VaaQuery) toBSON() *bson.D {
|
|||
}
|
||||
return &r
|
||||
}
|
||||
|
||||
func (q *VaaQuery) findOptions() *options.FindOptions {
|
||||
|
||||
sort := bson.D{{q.SortBy, q.GetSortInt()}}
|
||||
|
||||
return options.
|
||||
Find().
|
||||
SetSort(sort).
|
||||
SetLimit(q.Limit).
|
||||
SetSkip(q.Skip)
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ func (s *Service) findByIdWithPayload(ctx context.Context, chain vaa.ChainID, em
|
|||
// GetVaaCount get a list a list of vaa count grouped by chainID.
|
||||
func (s *Service) GetVaaCount(ctx context.Context, p *pagination.Pagination) (*response.Response[[]*VaaStats], error) {
|
||||
if p == nil {
|
||||
p = pagination.FirstPage()
|
||||
p = pagination.Default()
|
||||
}
|
||||
query := Query().SetPagination(p)
|
||||
stats, err := s.repo.GetVaaCount(ctx, query)
|
||||
|
|
|
@ -2,52 +2,15 @@ package pagination
|
|||
|
||||
// Pagination definition.
|
||||
type Pagination struct {
|
||||
Offset int64
|
||||
PageSize int64
|
||||
Skip int64
|
||||
Limit int64
|
||||
SortOrder string
|
||||
SortBy string
|
||||
}
|
||||
|
||||
// FirstPage return a *Pagination with default values offset and page size.
|
||||
func FirstPage() *Pagination {
|
||||
return &Pagination{Offset: 0, PageSize: 50}
|
||||
}
|
||||
|
||||
// BuildPagination create a new *Pagination.
|
||||
func BuildPagination(page, pageSize int64, sortOrder, sortBy string) *Pagination {
|
||||
p := Pagination{}
|
||||
p.SetPage(page).SetPageSize(pageSize).SetSortOrder(sortOrder).SetSortBy(sortBy)
|
||||
return &p
|
||||
}
|
||||
|
||||
// SetPageSize set the PageSize field of the Pagination struct.
|
||||
func (p *Pagination) SetPageSize(limit int64) *Pagination {
|
||||
p.PageSize = limit
|
||||
return p
|
||||
}
|
||||
|
||||
// SetOffset set the Offset field of the Pagination struct.
|
||||
func (p *Pagination) SetOffset(offset int64) *Pagination {
|
||||
p.Offset = offset
|
||||
return p
|
||||
}
|
||||
|
||||
// SetPage set the Page field of the Pagination struct.
|
||||
func (p *Pagination) SetPage(page int64) *Pagination {
|
||||
p.Offset = page * p.PageSize
|
||||
return p
|
||||
}
|
||||
|
||||
// SetSortOrder set the SortOrder field of the Pagination struct.
|
||||
func (p *Pagination) SetSortOrder(order string) *Pagination {
|
||||
p.SortOrder = order
|
||||
return p
|
||||
}
|
||||
|
||||
// SetSortBy set the SortBy field of the Pagination struct.
|
||||
func (p *Pagination) SetSortBy(by string) *Pagination {
|
||||
p.SortBy = by
|
||||
return p
|
||||
// Default returns a `*Pagination` with default values.
|
||||
func Default() *Pagination {
|
||||
return &Pagination{Skip: 0, Limit: 50}
|
||||
}
|
||||
|
||||
// GetSortInt mapping to mongodb sort values.
|
||||
|
|
|
@ -2,49 +2,50 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/internal/pagination"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/response"
|
||||
)
|
||||
|
||||
// ExtractPagination middleware invoke pagination.ExtractPagination.
|
||||
func ExtractPagination(c *fiber.Ctx) error {
|
||||
if c.Method() != http.MethodGet {
|
||||
return c.Next()
|
||||
}
|
||||
extractPagination(c)
|
||||
return c.Next()
|
||||
}
|
||||
// ExtractPagination parses pagination-related query parameters.
|
||||
func ExtractPagination(ctx *fiber.Ctx) (*pagination.Pagination, error) {
|
||||
|
||||
// extractPagination get pagination query params and build a *Pagination.
|
||||
func extractPagination(ctx *fiber.Ctx) (*pagination.Pagination, error) {
|
||||
// get page number
|
||||
pageNumberStr := ctx.Query("page", "0")
|
||||
pageNumber, err := strconv.ParseInt(pageNumberStr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err != nil || pageNumber < 0 {
|
||||
msg := `parameter 'page' must be a non-negative integer`
|
||||
return nil, response.NewInvalidParamError(ctx, msg, err)
|
||||
}
|
||||
|
||||
// get page size
|
||||
pageSizeStr := ctx.Query("pageSize", "50")
|
||||
pageSize, err := strconv.ParseInt(pageSizeStr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err != nil || pageSize <= 0 {
|
||||
msg := `parameter 'pageSize' must be a positive integer`
|
||||
return nil, response.NewInvalidParamError(ctx, msg, err)
|
||||
}
|
||||
skip := pageSize * pageNumber
|
||||
|
||||
// get sort order
|
||||
sortOrder := strings.ToUpper(ctx.Query("sortOrder", "DESC"))
|
||||
if sortOrder != "ASC" && sortOrder != "DESC" {
|
||||
msg := `parameter 'sortOrder' must either be 'ASC' or 'DESC'`
|
||||
return nil, response.NewInvalidParamError(ctx, msg, nil)
|
||||
}
|
||||
|
||||
sortOrder := ctx.Query("sortOrder", "DESC")
|
||||
// `sortBy` is currently not exposed as a parameter, but could be in the future.
|
||||
sortBy := ctx.Query("sortBy", "indexedAt")
|
||||
|
||||
p := pagination.BuildPagination(pageNumber, pageSize, sortOrder, sortBy)
|
||||
ctx.Locals("pagination", p)
|
||||
// initialize the result struct and return
|
||||
p := &pagination.Pagination{
|
||||
Skip: skip,
|
||||
Limit: pageSize,
|
||||
SortOrder: sortOrder,
|
||||
SortBy: sortBy,
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// GetPaginationFromContext get pagination from context.
|
||||
func GetPaginationFromContext(ctx *fiber.Ctx) *pagination.Pagination {
|
||||
p := ctx.Locals("pagination")
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return p.(*pagination.Pagination)
|
||||
}
|
||||
|
|
|
@ -14,128 +14,189 @@ import (
|
|||
|
||||
// ExtractChainID get chain parameter from route path.
|
||||
func ExtractChainID(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, error) {
|
||||
|
||||
chain, err := c.ParamsInt("chain")
|
||||
if err != nil {
|
||||
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
||||
l.Error("failed to get chain parameter", zap.Error(err), zap.Int("chain", chain),
|
||||
zap.String("requestID", requestID))
|
||||
l.Error("failed to get chain parameter",
|
||||
zap.Error(err),
|
||||
zap.Int("chain", chain),
|
||||
zap.String("requestID", requestID),
|
||||
)
|
||||
|
||||
return vaa.ChainIDUnset, response.NewInvalidParamError(c, "WRONG CHAIN ID", errors.WithStack(err))
|
||||
}
|
||||
|
||||
return vaa.ChainID(chain), nil
|
||||
}
|
||||
|
||||
// ExtractEmitterAddr get emitter parameter from route path.
|
||||
func ExtractEmitterAddr(c *fiber.Ctx, l *zap.Logger) (*vaa.Address, error) {
|
||||
|
||||
emitterStr := c.Params("emitter")
|
||||
|
||||
emitter, err := vaa.StringToAddress(emitterStr)
|
||||
if err != nil {
|
||||
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
||||
l.Error("failed to covert emitter to address", zap.Error(err), zap.String("emitterStr", emitterStr),
|
||||
zap.String("requestID", requestID))
|
||||
l.Error("failed to convert emitter to address",
|
||||
zap.Error(err),
|
||||
zap.String("emitterStr", emitterStr),
|
||||
zap.String("requestID", requestID),
|
||||
)
|
||||
return nil, response.NewInvalidParamError(c, "MALFORMED EMITTER_ADDR", errors.WithStack(err))
|
||||
}
|
||||
|
||||
return &emitter, nil
|
||||
}
|
||||
|
||||
// ExtractSequence get sequence parameter from route path.
|
||||
func ExtractSequence(c *fiber.Ctx, l *zap.Logger) (uint64, error) {
|
||||
|
||||
sequence := c.Params("sequence")
|
||||
|
||||
seq, err := strconv.ParseUint(sequence, 10, 64)
|
||||
if err != nil {
|
||||
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
||||
l.Error("failed to get sequence parameter", zap.Error(err), zap.String("sequence", sequence),
|
||||
zap.String("requestID", requestID))
|
||||
l.Error("failed to get sequence parameter",
|
||||
zap.Error(err),
|
||||
zap.String("sequence", sequence),
|
||||
zap.String("requestID", requestID),
|
||||
)
|
||||
return 0, response.NewInvalidParamError(c, "MALFORMED SEQUENCE NUMBER", errors.WithStack(err))
|
||||
}
|
||||
|
||||
return seq, nil
|
||||
}
|
||||
|
||||
// ExtractGuardianAddress get guardian address from route path.
|
||||
func ExtractGuardianAddress(c *fiber.Ctx, l *zap.Logger) (string, error) {
|
||||
//TODO: check guardianAddress [vaa.StringToAddress(emitterStr)]
|
||||
guardianAddress := c.Params("guardian_address")
|
||||
if guardianAddress == "" {
|
||||
|
||||
// read the address from query params
|
||||
tmp := c.Params("guardian_address")
|
||||
if tmp == "" {
|
||||
return "", response.NewInvalidParamError(c, "MALFORMED GUARDIAN ADDR", nil)
|
||||
}
|
||||
return guardianAddress, nil
|
||||
|
||||
// validate the address using the SDK
|
||||
guardianAddress, err := vaa.StringToAddress(tmp)
|
||||
if err != nil {
|
||||
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
||||
l.Error("failed to decode guardian address",
|
||||
zap.Error(err),
|
||||
zap.String("requestID", requestID),
|
||||
)
|
||||
return "", response.NewInvalidParamError(c, "MALFORMED GUARDIAN ADDR", errors.WithStack(err))
|
||||
}
|
||||
|
||||
// make sure the address length is the expected
|
||||
addr := guardianAddress.String()
|
||||
if len(addr) != 64 {
|
||||
return "", response.NewInvalidParamError(c, "MALFORMED GUARDIAN ADDR", nil)
|
||||
}
|
||||
|
||||
// the address returned by the SDK has 24 leading zeroes
|
||||
return addr[24:], nil
|
||||
}
|
||||
|
||||
// ExtractVAAParams get VAA chain, address from route path.
|
||||
func ExtractVAAChainIDEmitter(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, *vaa.Address, error) {
|
||||
|
||||
chainID, err := ExtractChainID(c, l)
|
||||
if err != nil {
|
||||
return vaa.ChainIDUnset, nil, err
|
||||
}
|
||||
|
||||
address, err := ExtractEmitterAddr(c, l)
|
||||
if err != nil {
|
||||
return chainID, nil, err
|
||||
}
|
||||
|
||||
return chainID, address, nil
|
||||
}
|
||||
|
||||
// ExtractVAAParams get VAAA chain, address and sequence from route path.
|
||||
func ExtractVAAParams(c *fiber.Ctx, l *zap.Logger) (vaa.ChainID, *vaa.Address, uint64, error) {
|
||||
|
||||
chainID, err := ExtractChainID(c, l)
|
||||
if err != nil {
|
||||
return vaa.ChainIDUnset, nil, 0, err
|
||||
}
|
||||
|
||||
address, err := ExtractEmitterAddr(c, l)
|
||||
if err != nil {
|
||||
return chainID, nil, 0, err
|
||||
}
|
||||
|
||||
seq, err := ExtractSequence(c, l)
|
||||
if err != nil {
|
||||
return chainID, address, 0, err
|
||||
}
|
||||
|
||||
return chainID, address, seq, nil
|
||||
}
|
||||
|
||||
// ExtractObservationSigner get signer from route path.
|
||||
func ExtractObservationSigner(c *fiber.Ctx, l *zap.Logger) (*vaa.Address, error) {
|
||||
|
||||
signer := c.Params("signer")
|
||||
|
||||
signerAddr, err := vaa.StringToAddress(signer)
|
||||
if err != nil {
|
||||
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
||||
l.Error("failed to covert signer to address", zap.Error(err), zap.String("signer", signer),
|
||||
zap.String("requestID", requestID))
|
||||
l.Error("failed to covert signer to address",
|
||||
zap.Error(err),
|
||||
zap.String("signer", signer),
|
||||
zap.String("requestID", requestID),
|
||||
)
|
||||
return nil, response.NewInvalidParamError(c, "MALFORMED SIGNER", errors.WithStack(err))
|
||||
}
|
||||
|
||||
return &signerAddr, nil
|
||||
}
|
||||
|
||||
// ExtractObservationHash get a hash from route path.
|
||||
func ExtractObservationHash(c *fiber.Ctx, l *zap.Logger) (string, error) {
|
||||
|
||||
hash := c.Params("hash")
|
||||
if hash == "" {
|
||||
return "", response.NewInvalidParamError(c, "MALFORMED HASH", nil)
|
||||
}
|
||||
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
// GetTxHash get txHash parameter from query param.
|
||||
func GetTxHash(c *fiber.Ctx, l *zap.Logger) (*vaa.Address, error) {
|
||||
|
||||
txHash := c.Query("txHash")
|
||||
if txHash == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
txHashAddr, err := vaa.StringToAddress(txHash)
|
||||
if err != nil {
|
||||
requestID := fmt.Sprintf("%v", c.Locals("requestid"))
|
||||
l.Error("failed to covert txHash to address", zap.Error(err), zap.String("txHash", txHash),
|
||||
zap.String("requestID", requestID))
|
||||
l.Error("failed to covert txHash to address",
|
||||
zap.Error(err),
|
||||
zap.String("txHash", txHash),
|
||||
zap.String("requestID", requestID),
|
||||
)
|
||||
return nil, response.NewInvalidParamError(c, "MALFORMED TX HASH", errors.WithStack(err))
|
||||
}
|
||||
|
||||
return &txHashAddr, nil
|
||||
}
|
||||
|
||||
// ExtractParsedPayload get parsedPayload query parameter.
|
||||
func ExtractParsedPayload(c *fiber.Ctx, l *zap.Logger) (bool, error) {
|
||||
|
||||
parsedPayloadStr := c.Query("parsedPayload", "false")
|
||||
|
||||
parsedPayload, err := strconv.ParseBool(parsedPayloadStr)
|
||||
if err != nil {
|
||||
return false, response.NewInvalidQueryParamError(c, "INVALID <parsedPayload> QUERY PARAMETER", errors.WithStack(err))
|
||||
}
|
||||
|
||||
return parsedPayload, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -32,11 +32,17 @@ func NewController(serv *governor.Service, logger *zap.Logger) *Controller {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/config [get]
|
||||
func (c *Controller) FindGovernorConfigurations(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
governorConfigs, err := c.srv.FindGovernorConfig(ctx.Context(), p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(governorConfigs)
|
||||
}
|
||||
|
||||
|
@ -52,15 +58,22 @@ func (c *Controller) FindGovernorConfigurations(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/config/:guardian_address [get]
|
||||
func (c *Controller) FindGovernorConfigurationByGuardianAddress(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
guardianAddress, err := middleware.ExtractGuardianAddress(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
govConfig, err := c.srv.FindGovernorConfigByGuardianAddress(ctx.Context(), guardianAddress, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(govConfig)
|
||||
}
|
||||
|
||||
|
@ -76,11 +89,17 @@ func (c *Controller) FindGovernorConfigurationByGuardianAddress(ctx *fiber.Ctx)
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/status [get]
|
||||
func (c *Controller) FindGovernorStatus(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
governorStatus, err := c.srv.FindGovernorStatus(ctx.Context(), p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(governorStatus)
|
||||
}
|
||||
|
||||
|
@ -96,15 +115,22 @@ func (c *Controller) FindGovernorStatus(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/status/:guardian_address [get]
|
||||
func (c *Controller) FindGovernorStatusByGuardianAddress(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
guardianAddress, err := middleware.ExtractGuardianAddress(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
govStatus, err := c.srv.FindGovernorStatusByGuardianAddress(ctx.Context(), guardianAddress, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(govStatus)
|
||||
}
|
||||
|
||||
|
@ -120,11 +146,17 @@ func (c *Controller) FindGovernorStatusByGuardianAddress(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/limit [get]
|
||||
func (c *Controller) GetGovernorLimit(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
governorLimit, err := c.srv.GetGovernorLimit(ctx.Context(), p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(governorLimit)
|
||||
}
|
||||
|
||||
|
@ -140,11 +172,17 @@ func (c *Controller) GetGovernorLimit(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/notional/limit [get]
|
||||
func (c *Controller) FindNotionalLimit(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notionalLimit, err := c.srv.FindNotionalLimit(ctx.Context(), p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(notionalLimit)
|
||||
}
|
||||
|
||||
|
@ -160,15 +198,22 @@ func (c *Controller) FindNotionalLimit(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/notional/limit/:chain [get]
|
||||
func (c *Controller) GetNotionalLimitByChainID(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainID, err := middleware.ExtractChainID(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notionalLimit, err := c.srv.GetNotionalLimitByChainID(ctx.Context(), p, chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(notionalLimit)
|
||||
}
|
||||
|
||||
|
@ -184,11 +229,17 @@ func (c *Controller) GetNotionalLimitByChainID(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/notional/available [get]
|
||||
func (c *Controller) GetAvailableNotional(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
notionalAvaialabilies, err := c.srv.GetAvailableNotional(ctx.Context(), p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(notionalAvaialabilies)
|
||||
}
|
||||
|
||||
|
@ -204,15 +255,22 @@ func (c *Controller) GetAvailableNotional(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/notional/available/:chain [get]
|
||||
func (c *Controller) GetAvailableNotionalByChainID(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainID, err := middleware.ExtractChainID(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response, err := c.srv.GetAvailableNotionalByChainID(ctx.Context(), p, chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(response)
|
||||
}
|
||||
|
||||
|
@ -228,15 +286,22 @@ func (c *Controller) GetAvailableNotionalByChainID(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/max_available/:chain [get]
|
||||
func (c *Controller) GetMaxNotionalAvailableByChainID(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainID, err := middleware.ExtractChainID(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response, err := c.srv.GetMaxNotionalAvailableByChainID(ctx.Context(), p, chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(response)
|
||||
}
|
||||
|
||||
|
@ -252,11 +317,17 @@ func (c *Controller) GetMaxNotionalAvailableByChainID(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/enqueued_vaas/ [get]
|
||||
func (c *Controller) GetEnqueueVaas(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enqueuedVaas, err := c.srv.GetEnqueueVass(ctx.Context(), p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(enqueuedVaas)
|
||||
}
|
||||
|
||||
|
@ -272,14 +343,21 @@ func (c *Controller) GetEnqueueVaas(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/governor/enqueued_vaas/:chain [get]
|
||||
func (c *Controller) GetEnqueuedVaasByChainID(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainID, err := middleware.ExtractChainID(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enqueuedVaas, err := c.srv.GetEnqueueVassByChainID(ctx.Context(), p, chainID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(enqueuedVaas)
|
||||
}
|
||||
|
|
|
@ -36,11 +36,17 @@ func NewController(srv *observations.Service, logger *zap.Logger) *Controller {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/observations [get]
|
||||
func (c *Controller) FindAll(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obs, err := c.srv.FindAll(ctx.Context(), p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(obs)
|
||||
}
|
||||
|
||||
|
@ -56,15 +62,22 @@ func (c *Controller) FindAll(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/observations/:chain [get]
|
||||
func (c *Controller) FindAllByChain(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainID, err := middleware.ExtractChainID(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
obs, err := c.srv.FindByChain(ctx.Context(), chainID, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(obs)
|
||||
}
|
||||
|
||||
|
@ -80,7 +93,12 @@ func (c *Controller) FindAllByChain(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/observations/:chain/:emitter [get]
|
||||
func (c *Controller) FindAllByEmitter(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainID, addr, err := middleware.ExtractVAAChainIDEmitter(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -90,6 +108,7 @@ func (c *Controller) FindAllByEmitter(ctx *fiber.Ctx) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(obs)
|
||||
}
|
||||
|
||||
|
@ -105,7 +124,12 @@ func (c *Controller) FindAllByEmitter(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/observations/:chain/:emitter/:sequence [get]
|
||||
func (c *Controller) FindAllByVAA(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainID, addr, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -115,6 +139,7 @@ func (c *Controller) FindAllByVAA(ctx *fiber.Ctx) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(obs)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
infrasvc "github.com/wormhole-foundation/wormhole-explorer/api/handlers/infrastructure"
|
||||
obssvc "github.com/wormhole-foundation/wormhole-explorer/api/handlers/observations"
|
||||
vaasvc "github.com/wormhole-foundation/wormhole-explorer/api/handlers/vaa"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/middleware"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/wormscan/governor"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/wormscan/infrastructure"
|
||||
"github.com/wormhole-foundation/wormhole-explorer/api/routes/wormscan/observations"
|
||||
|
@ -46,7 +45,6 @@ func RegisterRoutes(
|
|||
// Set up route handlers
|
||||
api := app.Group("/api/v1")
|
||||
api.Use(cors.New()) // TODO CORS restrictions?
|
||||
api.Use(middleware.ExtractPagination)
|
||||
|
||||
// monitoring
|
||||
api.Get("/health", infrastructureCtrl.HealthCheck)
|
||||
|
|
|
@ -38,7 +38,10 @@ func NewController(serv *vaa.Service, logger *zap.Logger) *Controller {
|
|||
// @Router /api/v1/vaas/ [get]
|
||||
func (c *Controller) FindAll(ctx *fiber.Ctx) error {
|
||||
|
||||
pagination := middleware.GetPaginationFromContext(ctx)
|
||||
pagination, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txHash, err := middleware.GetTxHash(ctx, c.logger)
|
||||
if err != nil {
|
||||
|
@ -81,15 +84,22 @@ func (c *Controller) FindAll(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/vaas/{chain_id} [get]
|
||||
func (c *Controller) FindByChain(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainID, err := middleware.ExtractChainID(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vaas, err := c.srv.FindByChain(ctx.Context(), chainID, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(vaas)
|
||||
}
|
||||
|
||||
|
@ -107,15 +117,22 @@ func (c *Controller) FindByChain(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/vaas/{chain_id}/{emitter} [get]
|
||||
func (c *Controller) FindByEmitter(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainID, emitter, err := middleware.ExtractVAAChainIDEmitter(ctx, c.logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vaas, err := c.srv.FindByEmitter(ctx.Context(), chainID, *emitter, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(vaas)
|
||||
}
|
||||
|
||||
|
@ -165,10 +182,16 @@ func (c *Controller) FindById(ctx *fiber.Ctx) error {
|
|||
// @Failure 500
|
||||
// @Router /api/v1/vaas/vaa-counts [get]
|
||||
func (c *Controller) GetVaaCount(ctx *fiber.Ctx) error {
|
||||
p := middleware.GetPaginationFromContext(ctx)
|
||||
|
||||
p, err := middleware.ExtractPagination(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vaas, err := c.srv.GetVaaCount(ctx.Context(), p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctx.JSON(vaas)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue