[API] Add parsed payloads to `GET /api/v1/vaas` (#113)
Add query parameter `parsedPayload` to the endpoint `GET /api/v1/vaas`.
This commit is contained in:
parent
1c7e58a27a
commit
ed0985d502
|
@ -93,54 +93,69 @@ func (r *Repository) FindOne(ctx context.Context, q *VaaQuery) (*VaaDoc, error)
|
||||||
return &vaaDoc, err
|
return &vaaDoc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVaaWithPayload get a vaa with payload if it exists.
|
// FindVaasWithPayload returns VAAs that include a parsed payload.
|
||||||
// The input parameter [q *VaaQuery] define the filters to apply in the query.
|
// The input parameter `q` defines the filters to be applied in the query.
|
||||||
func (r *Repository) GetVaaWithPayload(ctx context.Context, q *VaaQuery) (*VaaWithPayload, error) {
|
func (r *Repository) FindVaasWithPayload(
|
||||||
|
ctx context.Context,
|
||||||
|
q *VaaQuery,
|
||||||
|
) ([]*VaaWithPayload, error) {
|
||||||
|
|
||||||
|
// build a query pipeline based on input parameters
|
||||||
|
var pipeline mongo.Pipeline
|
||||||
|
{
|
||||||
|
// filter by emitterChain
|
||||||
|
if q.chainId != 0 {
|
||||||
|
pipeline = append(pipeline, bson.D{
|
||||||
|
{"$match", bson.D{bson.E{"emitterChain", q.chainId}}},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter by emitterAddr
|
||||||
|
if q.emitter != "" {
|
||||||
|
pipeline = append(pipeline, bson.D{
|
||||||
|
{"$match", bson.D{bson.E{"emitterAddr", q.emitter}}},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter by sequence
|
||||||
|
if q.sequence != "" {
|
||||||
|
pipeline = append(pipeline, bson.D{
|
||||||
|
{"$match", bson.D{bson.E{"sequence", q.sequence}}},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// left outer join on the `parsedVaa` collection
|
||||||
|
pipeline = append(pipeline, bson.D{
|
||||||
|
{"$lookup", bson.D{
|
||||||
|
{"from", "parsedVaa"},
|
||||||
|
{"localField", "_id"},
|
||||||
|
{"foreignField", "_id"},
|
||||||
|
{"as", "payload"},
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
|
||||||
|
// add parsed payload fields
|
||||||
|
pipeline = append(pipeline, bson.D{
|
||||||
|
{"$addFields", bson.D{
|
||||||
|
{"payload", bson.M{
|
||||||
|
"$arrayElemAt": []interface{}{"$payload.result", 0},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
|
||||||
|
// limit size of results
|
||||||
|
pipeline = append(pipeline, bson.D{
|
||||||
|
{"$limit", q.Pagination.PageSize},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute the aggregation pipeline
|
||||||
var err error
|
var err error
|
||||||
var cur *mongo.Cursor
|
var cur *mongo.Cursor
|
||||||
|
|
||||||
matchStage1 := bson.D{
|
|
||||||
{Key: "$match", Value: bson.D{bson.E{Key: "emitterChain", Value: q.chainId}}},
|
|
||||||
}
|
|
||||||
|
|
||||||
matchStage2 := bson.D{
|
|
||||||
{Key: "$match", Value: bson.D{bson.E{Key: "emitterAddr", Value: q.emitter}}},
|
|
||||||
}
|
|
||||||
|
|
||||||
matchStage3 := bson.D{
|
|
||||||
{Key: "$match", Value: bson.D{bson.E{Key: "sequence", Value: q.sequence}}},
|
|
||||||
}
|
|
||||||
|
|
||||||
lookupStage2 := bson.D{
|
|
||||||
{Key: "$lookup", Value: bson.D{
|
|
||||||
{Key: "from", Value: "parsedVaa"},
|
|
||||||
{Key: "localField", Value: "_id"},
|
|
||||||
{Key: "foreignField", Value: "_id"},
|
|
||||||
{Key: "as", Value: "payload"},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
addFieldsStage3 := bson.D{
|
|
||||||
{Key: "$addFields", Value: bson.D{
|
|
||||||
{Key: "payload", Value: bson.M{
|
|
||||||
"$arrayElemAt": []interface{}{"$payload.result", 0},
|
|
||||||
}},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
pipeLine := mongo.Pipeline{
|
|
||||||
matchStage1,
|
|
||||||
matchStage2,
|
|
||||||
matchStage3,
|
|
||||||
lookupStage2,
|
|
||||||
addFieldsStage3,
|
|
||||||
}
|
|
||||||
|
|
||||||
// execute aggregate operations.
|
|
||||||
if q.chainId == vaa.ChainIDPythNet {
|
if q.chainId == vaa.ChainIDPythNet {
|
||||||
cur, err = r.collections.vaasPythnet.Aggregate(ctx, pipeLine)
|
cur, err = r.collections.vaasPythnet.Aggregate(ctx, pipeline)
|
||||||
} else {
|
} else {
|
||||||
cur, err = r.collections.vaas.Aggregate(ctx, pipeLine)
|
cur, err = r.collections.vaas.Aggregate(ctx, pipeline)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
||||||
|
@ -149,30 +164,20 @@ func (r *Repository) GetVaaWithPayload(ctx context.Context, q *VaaQuery) (*VaaWi
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// decode cursor to array vaa with payload
|
// read results from cursor
|
||||||
var vaasWithPayload []*VaaWithPayload
|
var vaasWithPayload []*VaaWithPayload
|
||||||
err = cur.All(ctx, &vaasWithPayload)
|
err = cur.All(ctx, &vaasWithPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
||||||
r.logger.Error("failed decoding cursor to []*VaaWithPayload", zap.Error(err), zap.Any("q", q),
|
r.logger.Error("failed decoding cursor to []*VaaWithPayload",
|
||||||
zap.String("requestID", requestID))
|
zap.Error(err),
|
||||||
|
zap.Any("q", q),
|
||||||
|
zap.String("requestID", requestID),
|
||||||
|
)
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check not found
|
return vaasWithPayload, nil
|
||||||
if len(vaasWithPayload) == 0 {
|
|
||||||
return nil, errs.ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// check can not get more that one field in the response.
|
|
||||||
if len(vaasWithPayload) > 1 {
|
|
||||||
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
|
||||||
r.logger.Error("can not get more that one vaa by chainID/address/sequence", zap.Any("q", q),
|
|
||||||
zap.String("requestID", requestID))
|
|
||||||
return nil, errs.ErrInternalError
|
|
||||||
}
|
|
||||||
|
|
||||||
return vaasWithPayload[0], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVaaCount get a count of vaa by chainID.
|
// GetVaaCount get a count of vaa by chainID.
|
||||||
|
@ -180,7 +185,10 @@ func (r *Repository) GetVaaCount(ctx context.Context, q *VaaQuery) ([]*VaaStats,
|
||||||
if q == nil {
|
if q == nil {
|
||||||
q = Query()
|
q = Query()
|
||||||
}
|
}
|
||||||
sort := bson.D{{q.SortBy, q.GetSortInt()}}
|
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(), options.Find().SetLimit(q.PageSize).SetSkip(q.Offset).SetSort(sort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
||||||
|
|
|
@ -27,17 +27,57 @@ func NewService(r *Repository, getCacheFunc cache.CacheGetFunc, logger *zap.Logg
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindAll get all the the vaa.
|
// FindAll get all the the vaa.
|
||||||
func (s *Service) FindAll(ctx context.Context, p *pagination.Pagination, txHash *vaa.Address) (*response.Response[[]*VaaDoc], error) {
|
func (s *Service) FindAll(
|
||||||
|
ctx context.Context,
|
||||||
|
p *pagination.Pagination,
|
||||||
|
txHash *vaa.Address,
|
||||||
|
includeParsedPayload bool,
|
||||||
|
) (*response.Response[[]*VaaWithPayload], error) {
|
||||||
|
|
||||||
if p == nil {
|
if p == nil {
|
||||||
p = pagination.FirstPage()
|
p = pagination.FirstPage()
|
||||||
}
|
}
|
||||||
|
|
||||||
query := Query().SetPagination(p)
|
query := Query().SetPagination(p)
|
||||||
if txHash != nil {
|
if txHash != nil {
|
||||||
query = query.SetTxHash(txHash.String())
|
query = query.SetTxHash(txHash.String())
|
||||||
}
|
}
|
||||||
vaas, err := s.repo.Find(ctx, query)
|
|
||||||
res := response.Response[[]*VaaDoc]{Data: vaas}
|
if includeParsedPayload {
|
||||||
return &res, err
|
vaas, err := s.repo.FindVaasWithPayload(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response.Response[[]*VaaWithPayload]{Data: vaas}, nil
|
||||||
|
|
||||||
|
} else {
|
||||||
|
vaas, err := s.repo.Find(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var vaasWithPayload []*VaaWithPayload
|
||||||
|
for i := range vaas {
|
||||||
|
vaaWithPayload := VaaWithPayload{
|
||||||
|
ID: vaas[i].ID,
|
||||||
|
Version: vaas[i].Version,
|
||||||
|
EmitterChain: vaas[i].EmitterChain,
|
||||||
|
EmitterAddr: vaas[i].EmitterAddr,
|
||||||
|
Sequence: vaas[i].Sequence,
|
||||||
|
GuardianSetIndex: vaas[i].GuardianSetIndex,
|
||||||
|
Timestamp: vaas[i].Timestamp,
|
||||||
|
IndexedAt: vaas[i].IndexedAt,
|
||||||
|
UpdatedAt: vaas[i].UpdatedAt,
|
||||||
|
Vaa: vaas[i].Vaa,
|
||||||
|
}
|
||||||
|
|
||||||
|
vaasWithPayload = append(vaasWithPayload, &vaaWithPayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := response.Response[[]*VaaWithPayload]{Data: vaasWithPayload}
|
||||||
|
return &resp, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindByChain get all the vaa by chainID.
|
// FindByChain get all the vaa by chainID.
|
||||||
|
@ -57,7 +97,14 @@ func (s *Service) FindByEmitter(ctx context.Context, chain vaa.ChainID, emitter
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the parameter [payload] is true, the parse payload is added in the response.
|
// If the parameter [payload] is true, the parse payload is added in the response.
|
||||||
func (s *Service) FindById(ctx context.Context, chain vaa.ChainID, emitter vaa.Address, seq string, payload bool) (*response.Response[*VaaWithPayload], error) {
|
func (s *Service) FindById(
|
||||||
|
ctx context.Context,
|
||||||
|
chain vaa.ChainID,
|
||||||
|
emitter vaa.Address,
|
||||||
|
seq string,
|
||||||
|
payload bool,
|
||||||
|
) (*response.Response[*VaaWithPayload], error) {
|
||||||
|
|
||||||
// check vaa sequence indexed
|
// check vaa sequence indexed
|
||||||
isVaaNotIndexed := s.discardVaaNotIndexed(ctx, chain, emitter, seq)
|
isVaaNotIndexed := s.discardVaaNotIndexed(ctx, chain, emitter, seq)
|
||||||
if isVaaNotIndexed {
|
if isVaaNotIndexed {
|
||||||
|
@ -99,7 +146,22 @@ func (s *Service) findById(ctx context.Context, chain vaa.ChainID, emitter vaa.A
|
||||||
// findByIdWithPayload get a vaa with payload data by chainID, emitter address and sequence number.
|
// findByIdWithPayload get a vaa with payload data by chainID, emitter address and sequence number.
|
||||||
func (s *Service) findByIdWithPayload(ctx context.Context, chain vaa.ChainID, emitter vaa.Address, seq string) (*VaaWithPayload, error) {
|
func (s *Service) findByIdWithPayload(ctx context.Context, chain vaa.ChainID, emitter vaa.Address, seq string) (*VaaWithPayload, error) {
|
||||||
query := Query().SetChain(chain).SetEmitter(emitter.String()).SetSequence(seq)
|
query := Query().SetChain(chain).SetEmitter(emitter.String()).SetSequence(seq)
|
||||||
return s.repo.GetVaaWithPayload(ctx, query)
|
|
||||||
|
vaas, err := s.repo.FindVaasWithPayload(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if len(vaas) == 0 {
|
||||||
|
return nil, errs.ErrNotFound
|
||||||
|
} else if len(vaas) > 1 {
|
||||||
|
requestID := fmt.Sprintf("%v", ctx.Value("requestid"))
|
||||||
|
s.logger.Error("can not get more that one vaa by chainID/address/sequence",
|
||||||
|
zap.Any("q", query),
|
||||||
|
zap.String("requestID", requestID),
|
||||||
|
)
|
||||||
|
return nil, errs.ErrInternalError
|
||||||
|
}
|
||||||
|
|
||||||
|
return vaas[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVaaCount get a list a list of vaa count grouped by chainID.
|
// GetVaaCount get a list a list of vaa count grouped by chainID.
|
||||||
|
|
|
@ -34,12 +34,20 @@ func NewController(serv *vaa.Service, logger *zap.Logger) *Controller {
|
||||||
// @Failure 500
|
// @Failure 500
|
||||||
// @Router /api/v1/vaas/ [get]
|
// @Router /api/v1/vaas/ [get]
|
||||||
func (c *Controller) FindAll(ctx *fiber.Ctx) error {
|
func (c *Controller) FindAll(ctx *fiber.Ctx) error {
|
||||||
|
|
||||||
p := middleware.GetPaginationFromContext(ctx)
|
p := middleware.GetPaginationFromContext(ctx)
|
||||||
|
|
||||||
txHash, err := middleware.GetTxHash(ctx, c.logger)
|
txHash, err := middleware.GetTxHash(ctx, c.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
vaas, err := c.srv.FindAll(ctx.Context(), p, txHash)
|
|
||||||
|
includeParsedPayload, err := middleware.ExtractParsedPayload(ctx, c.logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
vaas, err := c.srv.FindAll(ctx.Context(), p, txHash, includeParsedPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -111,17 +119,24 @@ func (c *Controller) FindByEmitter(ctx *fiber.Ctx) error {
|
||||||
// @Failure 500
|
// @Failure 500
|
||||||
// @Router /api/v1/vaas/{chain_id}/{emitter}/{seq}/{signer}/{hash} [get]
|
// @Router /api/v1/vaas/{chain_id}/{emitter}/{seq}/{signer}/{hash} [get]
|
||||||
func (c *Controller) FindById(ctx *fiber.Ctx) error {
|
func (c *Controller) FindById(ctx *fiber.Ctx) error {
|
||||||
|
|
||||||
chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
|
chainID, emitter, seq, err := middleware.ExtractVAAParams(ctx, c.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedPayload, err := middleware.ExtractParsedPayload(ctx, c.logger)
|
includeParsedPayload, err := middleware.ExtractParsedPayload(ctx, c.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
vaa, err := c.srv.FindById(ctx.Context(), chainID, *emitter, strconv.FormatUint(seq, 10), parsedPayload)
|
vaa, err := c.srv.FindById(
|
||||||
|
ctx.Context(),
|
||||||
|
chainID,
|
||||||
|
*emitter,
|
||||||
|
strconv.FormatUint(seq, 10),
|
||||||
|
includeParsedPayload,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue