diff --git a/cmd/api/handler/responses/constants.go b/cmd/api/handler/responses/constants.go index a98864fe..240be965 100644 --- a/cmd/api/handler/responses/constants.go +++ b/cmd/api/handler/responses/constants.go @@ -70,6 +70,7 @@ type Enums struct { MessageType []string `json:"message_type"` EventType []string `json:"event_type"` Categories []string `json:"categories"` + RollupTypes []string `json:"rollup_type"` } func NewEnums() Enums { @@ -78,5 +79,6 @@ func NewEnums() Enums { MessageType: types.MsgTypeNames(), EventType: types.EventTypeNames(), Categories: types.RollupCategoryNames(), + RollupTypes: types.RollupTypeNames(), } } diff --git a/cmd/api/handler/rollup.go b/cmd/api/handler/rollup.go index dac9f799..960bc323 100644 --- a/cmd/api/handler/rollup.go +++ b/cmd/api/handler/rollup.go @@ -38,6 +38,7 @@ type rollupList struct { Sort string `query:"sort" validate:"omitempty,oneof=asc desc"` SortBy string `query:"sort_by" validate:"omitempty,oneof=time blobs_count size fee"` Category StringArray `query:"category" validate:"omitempty,dive,category"` + Type StringArray `query:"type" validate:"omitempty,dive,type"` } func (p *rollupList) SetDefault() { @@ -80,12 +81,18 @@ func (handler RollupHandler) Leaderboard(c echo.Context) error { categories[i] = types.RollupCategory(req.Category[i]) } + rollupTypes := make([]types.RollupType, len(req.Type)) + for i := range rollupTypes { + rollupTypes[i] = types.RollupType(req.Type[i]) + } + rollups, err := handler.rollups.Leaderboard(c.Request().Context(), storage.LeaderboardFilters{ SortField: req.SortBy, Sort: pgSort(req.Sort), Limit: req.Limit, Offset: req.Offset, Category: categories, + Type: rollupTypes, }) if err != nil { return handleError(c, err, handler.rollups) @@ -103,6 +110,7 @@ type rollupDayList struct { Sort string `query:"sort" validate:"omitempty,oneof=asc desc"` SortBy string `query:"sort_by" validate:"omitempty,oneof=avg_size blobs_count total_size total_fee throughput namespace_count pfb_count mb_price"` Category StringArray `query:"category" validate:"omitempty,dive,category"` + Type StringArray `query:"type" validate:"omitempty,dive,type"` } func (p *rollupDayList) SetDefault() { @@ -145,12 +153,18 @@ func (handler RollupHandler) LeaderboardDay(c echo.Context) error { categories[i] = types.RollupCategory(req.Category[i]) } + rollupTypes := make([]types.RollupType, len(req.Type)) + for i := range rollupTypes { + rollupTypes[i] = types.RollupType(req.Type[i]) + } + rollups, err := handler.rollups.LeaderboardDay(c.Request().Context(), storage.LeaderboardFilters{ SortField: req.SortBy, Sort: pgSort(req.Sort), Limit: req.Limit, Offset: req.Offset, Category: categories, + Type: rollupTypes, }) if err != nil { return handleError(c, err, handler.rollups) diff --git a/cmd/api/handler/rollup_test.go b/cmd/api/handler/rollup_test.go index 7fd4cdfb..49327e4f 100644 --- a/cmd/api/handler/rollup_test.go +++ b/cmd/api/handler/rollup_test.go @@ -33,6 +33,7 @@ var ( Logo: "image.png", Slug: "test-rollup", Category: types.RollupCategoryNft, + Type: types.RollupTypeSettled, } testRollupWithStats = storage.RollupWithStats{ Rollup: testRollup, @@ -90,6 +91,7 @@ func (s *RollupTestSuite) TestLeaderboard() { q := make(url.Values) q.Add("sort_by", sort) q.Add("category", "nft,gaming") + q.Add("type", "sovereign") req := httptest.NewRequest(http.MethodGet, "/?"+q.Encode(), nil) rec := httptest.NewRecorder() @@ -106,6 +108,9 @@ func (s *RollupTestSuite) TestLeaderboard() { types.RollupCategoryNft, types.RollupCategoryGaming, }, + Type: []types.RollupType{ + types.RollupTypeSovereign, + }, }). Return([]storage.RollupWithStats{testRollupWithStats}, nil). Times(1) @@ -140,6 +145,7 @@ func (s *RollupTestSuite) TestLeaderboardDay() { q := make(url.Values) q.Add("sort_by", sort) q.Add("category", "nft,gaming") + q.Add("type", "sovereign") req := httptest.NewRequest(http.MethodGet, "/?"+q.Encode(), nil) rec := httptest.NewRecorder() @@ -156,6 +162,9 @@ func (s *RollupTestSuite) TestLeaderboardDay() { types.RollupCategoryNft, types.RollupCategoryGaming, }, + Type: []types.RollupType{ + types.RollupTypeSovereign, + }, }). Return([]storage.RollupWithDayStats{ { diff --git a/cmd/api/handler/validators.go b/cmd/api/handler/validators.go index 92d4de46..5c592292 100644 --- a/cmd/api/handler/validators.go +++ b/cmd/api/handler/validators.go @@ -35,6 +35,9 @@ func NewCelestiaApiValidator() *CelestiaApiValidator { if err := v.RegisterValidation("category", categoryValidator()); err != nil { panic(err) } + if err := v.RegisterValidation("type", typeValidator()); err != nil { + panic(err) + } return &CelestiaApiValidator{validator: v} } @@ -109,3 +112,10 @@ func categoryValidator() validator.Func { return err == nil } } + +func typeValidator() validator.Func { + return func(fl validator.FieldLevel) bool { + _, err := types.ParseRollupType(fl.Field().String()) + return err == nil + } +} diff --git a/internal/storage/postgres/rollup.go b/internal/storage/postgres/rollup.go index 5c735567..401e1cf5 100644 --- a/internal/storage/postgres/rollup.go +++ b/internal/storage/postgres/rollup.go @@ -46,6 +46,10 @@ func (r *Rollup) Leaderboard(ctx context.Context, fltrs storage.LeaderboardFilte query = query.Where("category IN (?)", bun.In(fltrs.Category)) } + if len(fltrs.Type) > 0 { + query = query.Where("type IN (?)", bun.In(fltrs.Type)) + } + query = sortScope(query, fltrs.SortField, fltrs.Sort) query = limitScope(query, fltrs.Limit) err = query.Scan(ctx, &rollups) @@ -72,6 +76,10 @@ func (r *Rollup) LeaderboardDay(ctx context.Context, fltrs storage.LeaderboardFi query = query.Where("category IN (?)", bun.In(fltrs.Category)) } + if len(fltrs.Type) > 0 { + query = query.Where("type IN (?)", bun.In(fltrs.Type)) + } + query = sortScope(query, fltrs.SortField, fltrs.Sort) query = limitScope(query, fltrs.Limit) err = query.Scan(ctx, &rollups) diff --git a/internal/storage/postgres/rollup_test.go b/internal/storage/postgres/rollup_test.go index f58da628..fd2d0d52 100644 --- a/internal/storage/postgres/rollup_test.go +++ b/internal/storage/postgres/rollup_test.go @@ -82,6 +82,42 @@ func (s *StorageTestSuite) TestRollupLeaderboardWithCategory() { } } +func (s *StorageTestSuite) TestRollupLeaderboardWithType() { + ctx, ctxCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer ctxCancel() + + _, err := s.storage.Connection().Exec(ctx, "REFRESH MATERIALIZED VIEW leaderboard;") + s.Require().NoError(err) + + for _, column := range []string{ + sizeColumn, blobsCountColumn, timeColumn, feeColumn, "", + } { + + rollups, err := s.storage.Rollup.Leaderboard(ctx, storage.LeaderboardFilters{ + SortField: column, + Sort: sdk.SortOrderDesc, + Limit: 10, + Offset: 0, + Type: []types.RollupType{types.RollupTypeSovereign}, + }) + s.Require().NoError(err, column) + s.Require().Len(rollups, 1, column) + + rollup := rollups[0] + s.Require().EqualValues("Rollup 3", rollup.Name, column) + s.Require().EqualValues("The third", rollup.Description, column) + s.Require().EqualValues(34, rollup.Size, column) + s.Require().EqualValues(3, rollup.BlobsCount, column) + s.Require().False(rollup.LastActionTime.IsZero()) + s.Require().False(rollup.FirstActionTime.IsZero()) + s.Require().Equal("7000", rollup.Fee.String()) + s.Require().EqualValues(0.6363636363636364, rollup.FeePct) + s.Require().EqualValues(0.42857142857142855, rollup.BlobsCountPct) + s.Require().EqualValues(0.3953488372093023, rollup.SizePct) + s.Require().EqualValues("sovereign", rollup.Type) + } +} + func (s *StorageTestSuite) TestRollupLeaderboardDay() { ctx, ctxCancel := context.WithTimeout(context.Background(), 5*time.Second) defer ctxCancel() diff --git a/internal/storage/rollup.go b/internal/storage/rollup.go index 641fa7ae..27b7c6cd 100644 --- a/internal/storage/rollup.go +++ b/internal/storage/rollup.go @@ -19,6 +19,7 @@ type LeaderboardFilters struct { Limit int Offset int Category []types.RollupCategory + Type []types.RollupType } //go:generate mockgen -source=$GOFILE -destination=mock/$GOFILE -package=mock -typed diff --git a/test/data/rollup.yml b/test/data/rollup.yml index 530ee2d3..d1e9e3df 100644 --- a/test/data/rollup.yml +++ b/test/data/rollup.yml @@ -7,6 +7,7 @@ logo: https://rollup1.com/image.png slug: rollup_1 category: finance + type: settled - id: 2 name: Rollup 2 description: The second @@ -16,6 +17,7 @@ logo: https://rollup2.com/image.png slug: rollup_2 category: gaming + type: settled - id: 3 name: Rollup 3 description: The third @@ -25,3 +27,4 @@ logo: https://rollup3.com/image.png slug: rollup_3 category: nft + type: sovereign