diff --git a/cmd/api/docs/docs.go b/cmd/api/docs/docs.go index 8c84f4a..d67fce8 100644 --- a/cmd/api/docs/docs.go +++ b/cmd/api/docs/docs.go @@ -954,6 +954,16 @@ const docTemplate = `{ "description": "Sort order", "name": "sort", "in": "query" + }, + { + "enum": [ + "id", + "size" + ], + "type": "string", + "description": "Field using for sorting. Default: id", + "name": "sort_by", + "in": "query" } ], "responses": { diff --git a/cmd/api/docs/swagger.json b/cmd/api/docs/swagger.json index b27328a..48877cb 100644 --- a/cmd/api/docs/swagger.json +++ b/cmd/api/docs/swagger.json @@ -944,6 +944,16 @@ "description": "Sort order", "name": "sort", "in": "query" + }, + { + "enum": [ + "id", + "size" + ], + "type": "string", + "description": "Field using for sorting. Default: id", + "name": "sort_by", + "in": "query" } ], "responses": { diff --git a/cmd/api/docs/swagger.yaml b/cmd/api/docs/swagger.yaml index e4dc553..611cb1d 100644 --- a/cmd/api/docs/swagger.yaml +++ b/cmd/api/docs/swagger.yaml @@ -1086,6 +1086,13 @@ paths: in: query name: sort type: string + - description: 'Field using for sorting. Default: id' + enum: + - id + - size + in: query + name: sort_by + type: string produces: - application/json responses: diff --git a/cmd/api/handler/rollup.go b/cmd/api/handler/rollup.go index df1debe..443e132 100644 --- a/cmd/api/handler/rollup.go +++ b/cmd/api/handler/rollup.go @@ -69,35 +69,58 @@ func (handler *RollupHandler) Get(c echo.Context) error { return c.JSON(http.StatusOK, responses.NewRollup(&rollup)) } +type listRollupsRequest struct { + Limit int `query:"limit" validate:"omitempty,min=1,max=100"` + Offset int `query:"offset" validate:"omitempty,min=0"` + Sort string `query:"sort" validate:"omitempty,oneof=asc desc"` + SortField string `query:"sort_by" validate:"omitempty,oneof=size id"` +} + +func (p *listRollupsRequest) SetDefault() { + if p.Limit == 0 { + p.Limit = 10 + } + if p.Sort == "" { + p.Sort = desc + } +} + // List godoc // // @Summary List rollups info // @Description List rollups info // @Tags rollup // @ID list-rollups -// @Param limit query integer false "Count of requested entities" mininum(1) maximum(100) -// @Param offset query integer false "Offset" mininum(1) -// @Param sort query string false "Sort order" Enums(asc, desc) +// @Param limit query integer false "Count of requested entities" mininum(1) maximum(100) +// @Param offset query integer false "Offset" mininum(1) +// @Param sort query string false "Sort order" Enums(asc, desc) +// @Param sort_by query string false "Field using for sorting. Default: id" Enums(id, size) // @Produce json // @Success 200 {array} responses.Rollup // @Failure 400 {object} Error // @Failure 500 {object} Error // @Router /v1/rollup [get] func (handler *RollupHandler) List(c echo.Context) error { - req, err := bindAndValidate[listRequest](c) + req, err := bindAndValidate[listRollupsRequest](c) if err != nil { return badRequestError(c, err) } req.SetDefault() - rollups, err := handler.rollups.List(c.Request().Context(), req.Limit, req.Offset, pgSort(req.Sort)) + fltrs := storage.RollupListFilter{ + Limit: req.Limit, + Offset: req.Offset, + SortOrder: pgSort(req.Sort), + SortField: req.Sort, + } + rollups, err := handler.rollups.ListExt(c.Request().Context(), fltrs) if err != nil { return handleError(c, err, handler.rollups) } response := make([]responses.Rollup, len(rollups)) for i := range rollups { - response[i] = responses.NewRollup(rollups[i]) + response[i] = responses.NewRollup(&rollups[i]) } return returnArray(c, response) diff --git a/internal/storage/mock/rollup.go b/internal/storage/mock/rollup.go index 4c3e1a6..8fb88ae 100644 --- a/internal/storage/mock/rollup.go +++ b/internal/storage/mock/rollup.go @@ -472,6 +472,45 @@ func (c *IRollupListCall) DoAndReturn(f func(context.Context, uint64, uint64, st return c } +// ListExt mocks base method. +func (m *MockIRollup) ListExt(ctx context.Context, fltrs storage.RollupListFilter) ([]storage.Rollup, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListExt", ctx, fltrs) + ret0, _ := ret[0].([]storage.Rollup) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListExt indicates an expected call of ListExt. +func (mr *MockIRollupMockRecorder) ListExt(ctx, fltrs any) *IRollupListExtCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListExt", reflect.TypeOf((*MockIRollup)(nil).ListExt), ctx, fltrs) + return &IRollupListExtCall{Call: call} +} + +// IRollupListExtCall wrap *gomock.Call +type IRollupListExtCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *IRollupListExtCall) Return(arg0 []storage.Rollup, arg1 error) *IRollupListExtCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *IRollupListExtCall) Do(f func(context.Context, storage.RollupListFilter) ([]storage.Rollup, error)) *IRollupListExtCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *IRollupListExtCall) DoAndReturn(f func(context.Context, storage.RollupListFilter) ([]storage.Rollup, error)) *IRollupListExtCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // ListRollupsByAddress mocks base method. func (m *MockIRollup) ListRollupsByAddress(ctx context.Context, addressId uint64, limit, offset int, sort storage0.SortOrder) ([]storage.RollupAddress, error) { m.ctrl.T.Helper() diff --git a/internal/storage/postgres/index.go b/internal/storage/postgres/index.go index 564aaf3..dc7504d 100644 --- a/internal/storage/postgres/index.go +++ b/internal/storage/postgres/index.go @@ -141,6 +141,14 @@ func createIndices(ctx context.Context, conn *database.Bun) error { Exec(ctx); err != nil { return err } + if _, err := tx.NewCreateIndex(). + IfNotExists(). + Model((*storage.Rollup)(nil)). + Index("rollup_size_idx"). + Column("size"). + Exec(ctx); err != nil { + return err + } // Rollup actions if _, err := tx.NewCreateIndex(). diff --git a/internal/storage/postgres/rollup.go b/internal/storage/postgres/rollup.go index eb367f0..b7669c3 100644 --- a/internal/storage/postgres/rollup.go +++ b/internal/storage/postgres/rollup.go @@ -97,3 +97,19 @@ func (r *Rollup) ListRollupsByAddress(ctx context.Context, addressId uint64, lim err = query.Scan(ctx) return } + +func (r *Rollup) ListExt(ctx context.Context, fltrs storage.RollupListFilter) (rollups []storage.Rollup, err error) { + query := r.DB().NewSelect().Model(&rollups) + + query = limitScope(query, fltrs.Limit) + switch fltrs.SortField { + case "size": + query = sortScope(query, "size", fltrs.SortOrder) + default: + query = sortScope(query, "id", fltrs.SortOrder) + } + query = offsetScope(query, fltrs.Offset) + + err = query.Scan(ctx) + return +} diff --git a/internal/storage/postgres/rollup_test.go b/internal/storage/postgres/rollup_test.go index 6b84873..b150fbf 100644 --- a/internal/storage/postgres/rollup_test.go +++ b/internal/storage/postgres/rollup_test.go @@ -8,6 +8,7 @@ import ( "encoding/hex" "time" + models "github.com/celenium-io/astria-indexer/internal/storage" "github.com/dipdup-net/indexer-sdk/pkg/storage" ) @@ -106,3 +107,40 @@ func (s *StorageTestSuite) TestListRollupsByAddress() { s.Require().NotNil(rollup.Rollup) s.Require().EqualValues(1, rollup.Rollup.Id) } + +func (s *StorageTestSuite) TestListExt() { + ctx, ctxCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer ctxCancel() + + for _, order := range []storage.SortOrder{ + storage.SortOrderAsc, + storage.SortOrderDesc, + } { + for _, field := range []string{ + "size", + "id", + "", + } { + rollups, err := s.storage.Rollup.ListExt(ctx, models.RollupListFilter{ + Limit: 1, + Offset: 0, + SortField: field, + SortOrder: order, + }) + s.Require().NoError(err) + s.Require().Len(rollups, 1) + + rollup := rollups[0] + + hash, err := hex.DecodeString("19ba8abb3e4b56a309df6756c47b97e298e3a72d88449d36a0fadb1ca7366539") + s.Require().NoError(err) + + s.Require().EqualValues(1, rollup.Id) + s.Require().EqualValues(hash, rollup.AstriaId) + s.Require().EqualValues(112, rollup.Size) + s.Require().EqualValues(1, rollup.ActionsCount) + s.Require().EqualValues(7316, rollup.FirstHeight) + } + } + +} diff --git a/internal/storage/rollup.go b/internal/storage/rollup.go index b7b750b..aeaffbf 100644 --- a/internal/storage/rollup.go +++ b/internal/storage/rollup.go @@ -24,6 +24,7 @@ type IRollup interface { ByHash(ctx context.Context, hash []byte) (Rollup, error) Addresses(ctx context.Context, rollupId uint64, limit, offset int, sort sdk.SortOrder) ([]RollupAddress, error) ListRollupsByAddress(ctx context.Context, addressId uint64, limit, offset int, sort sdk.SortOrder) ([]RollupAddress, error) + ListExt(ctx context.Context, fltrs RollupListFilter) ([]Rollup, error) } type Rollup struct { @@ -44,3 +45,10 @@ func (Rollup) TableName() string { func (r Rollup) String() string { return hex.EncodeToString(r.AstriaId) } + +type RollupListFilter struct { + Limit int + Offset int + SortField string + SortOrder sdk.SortOrder +}