[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:
agodnic 2023-02-09 14:28:39 -03:00 committed by GitHub
parent b033c04a13
commit dde87acf84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 299 additions and 131 deletions

View File

@ -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}
}

View File

@ -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)

View File

@ -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}
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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.

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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)
}