diff --git a/cmd/api/handlers/requests.go b/cmd/api/handlers/requests.go index 7b9c746d6..53d250b45 100644 --- a/cmd/api/handlers/requests.go +++ b/cmd/api/handlers/requests.go @@ -258,3 +258,10 @@ type ticketBalancesRequest struct { pageableRequest WithoutZeroBalances bool `binding:"omitempty" form:"skip_empty"` } + +type ticketUpdatesRequest struct { + pageableRequest + + Account string `binding:"omitempty,address" form:"account"` + TicketId *uint64 `binding:"omitempty" form:"ticket_id"` +} diff --git a/cmd/api/handlers/tickets.go b/cmd/api/handlers/tickets.go index b9f2015b2..3ad36f2de 100644 --- a/cmd/api/handlers/tickets.go +++ b/cmd/api/handlers/tickets.go @@ -15,10 +15,12 @@ import ( // @Description Get ticket updates for contract // @Tags contract // @ID get-contract-ticket-updates -// @Param network path string true "network" -// @Param address path string true "KT address" minlength(36) maxlength(36) -// @Param size query integer false "Updates count" mininum(1) maximum(10) -// @Param offset query integer false "Offset" mininum(1) +// @Param network path string true "network" +// @Param address path string true "KT address" minlength(36) maxlength(36) +// @Param account query string false "Address" minlength(36) maxlength(36) +// @Param ticket_id query integer false "Ticket id" mininum(0) +// @Param size query integer false "Updates count" mininum(1) maximum(10) +// @Param offset query integer false "Offset" mininum(1) // @Accept json // @Produce json // @Success 200 {array} TicketUpdate @@ -35,12 +37,21 @@ func GetContractTicketUpdates() gin.HandlerFunc { return } - var args pageableRequest + var args ticketUpdatesRequest if err := c.BindQuery(&args); handleError(c, ctx.Storage, err, http.StatusBadRequest) { return } - updates, err := ctx.Tickets.Updates(c.Request.Context(), req.Address, args.Size, args.Offset) + updates, err := ctx.Tickets.Updates(c.Request.Context(), + ticket.UpdatesRequest{ + Ticketer: req.Address, + Account: args.Account, + TicketId: args.TicketId, + Limit: args.Size, + Offset: args.Offset, + }, + ) + if handleError(c, ctx.Storage, err, 0) { return } diff --git a/cmd/indexer/indexer/indices.go b/cmd/indexer/indexer/indices.go index 76fc29248..74875cc9d 100644 --- a/cmd/indexer/indexer/indices.go +++ b/cmd/indexer/indexer/indices.go @@ -116,12 +116,18 @@ func (bi *BlockchainIndexer) createIndices(ctx context.Context) error { if err := bi.Storage.CreateIndex(ctx, "ticket_updates_ticket_id_idx", "ticket_id", ticketUpdate); err != nil { return err } + if err := bi.Storage.CreateIndex(ctx, "ticket_updates_account_id_idx", "account_id", ticketUpdate); err != nil { + return err + } // Tickets tickets := (*ticket.Ticket)(nil) if err := bi.Storage.CreateIndex(ctx, "ticket_level_idx", "level", tickets); err != nil { return err } + if err := bi.Storage.CreateIndex(ctx, "ticket_ticketer_id_idx", "ticketer_id", tickets); err != nil { + return err + } log.Info().Str("network", bi.Network.String()).Msg("database indices was created") diff --git a/internal/models/mock/ticket/mock.go b/internal/models/mock/ticket/mock.go index 675f00e05..61be6d115 100644 --- a/internal/models/mock/ticket/mock.go +++ b/internal/models/mock/ticket/mock.go @@ -118,18 +118,18 @@ func (c *RepositoryListCall) DoAndReturn(f func(context.Context, string, int64, } // Updates mocks base method. -func (m *MockRepository) Updates(ctx context.Context, ticketer string, limit, offset int64) ([]ticket.TicketUpdate, error) { +func (m *MockRepository) Updates(ctx context.Context, req ticket.UpdatesRequest) ([]ticket.TicketUpdate, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Updates", ctx, ticketer, limit, offset) + ret := m.ctrl.Call(m, "Updates", ctx, req) ret0, _ := ret[0].([]ticket.TicketUpdate) ret1, _ := ret[1].(error) return ret0, ret1 } // Updates indicates an expected call of Updates. -func (mr *MockRepositoryMockRecorder) Updates(ctx, ticketer, limit, offset any) *RepositoryUpdatesCall { +func (mr *MockRepositoryMockRecorder) Updates(ctx, req any) *RepositoryUpdatesCall { mr.mock.ctrl.T.Helper() - call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Updates", reflect.TypeOf((*MockRepository)(nil).Updates), ctx, ticketer, limit, offset) + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Updates", reflect.TypeOf((*MockRepository)(nil).Updates), ctx, req) return &RepositoryUpdatesCall{Call: call} } @@ -145,13 +145,13 @@ func (c *RepositoryUpdatesCall) Return(arg0 []ticket.TicketUpdate, arg1 error) * } // Do rewrite *gomock.Call.Do -func (c *RepositoryUpdatesCall) Do(f func(context.Context, string, int64, int64) ([]ticket.TicketUpdate, error)) *RepositoryUpdatesCall { +func (c *RepositoryUpdatesCall) Do(f func(context.Context, ticket.UpdatesRequest) ([]ticket.TicketUpdate, error)) *RepositoryUpdatesCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn -func (c *RepositoryUpdatesCall) DoAndReturn(f func(context.Context, string, int64, int64) ([]ticket.TicketUpdate, error)) *RepositoryUpdatesCall { +func (c *RepositoryUpdatesCall) DoAndReturn(f func(context.Context, ticket.UpdatesRequest) ([]ticket.TicketUpdate, error)) *RepositoryUpdatesCall { c.Call = c.Call.DoAndReturn(f) return c } diff --git a/internal/models/ticket/repository.go b/internal/models/ticket/repository.go index ead49ebd8..a3372e4e9 100644 --- a/internal/models/ticket/repository.go +++ b/internal/models/ticket/repository.go @@ -8,10 +8,18 @@ type BalanceRequest struct { WithoutZeroBalances bool } +type UpdatesRequest struct { + Account string + Ticketer string + TicketId *uint64 + Limit int64 + Offset int64 +} + //go:generate mockgen -source=$GOFILE -destination=../mock/ticket/mock.go -package=ticket -typed type Repository interface { List(ctx context.Context, ticketer string, limit, offset int64) ([]Ticket, error) - Updates(ctx context.Context, ticketer string, limit, offset int64) ([]TicketUpdate, error) + Updates(ctx context.Context, req UpdatesRequest) ([]TicketUpdate, error) UpdatesForOperation(ctx context.Context, operationId int64) ([]TicketUpdate, error) BalancesForAccount(ctx context.Context, accountId int64, req BalanceRequest) ([]Balance, error) } diff --git a/internal/postgres/tests/fixtures/accounts.yml b/internal/postgres/tests/fixtures/accounts.yml index c1e9992c7..aa7a3e552 100644 --- a/internal/postgres/tests/fixtures/accounts.yml +++ b/internal/postgres/tests/fixtures/accounts.yml @@ -736,6 +736,15 @@ events_count: 0 migrations_count: 0 ticket_updates_count: 0 +- address: tz2GGf91VB3gK7MaaP8Xua6ohhHAhH4hTC11 + id: 131 + last_action: 2022-01-25T16:59:07Z + level: 39 + operations_count: 2 + type: 1 + events_count: 0 + migrations_count: 0 + ticket_updates_count: 0 - address: KT1BrG24EYvtYGeGgd615Yzhvoa14BNPLZzu id: 132 last_action: 2022-01-25T16:59:07Z diff --git a/internal/postgres/tests/ticket_test.go b/internal/postgres/tests/ticket_test.go index 7def0b54b..3d8476794 100644 --- a/internal/postgres/tests/ticket_test.go +++ b/internal/postgres/tests/ticket_test.go @@ -5,13 +5,62 @@ import ( "time" "github.com/baking-bad/bcdhub/internal/models/ticket" + "github.com/baking-bad/bcdhub/internal/testsuite" ) -func (s *StorageTestSuite) TestTicketGet() { +func (s *StorageTestSuite) TestTicketUpdates() { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - updates, err := s.ticketUpdates.Updates(ctx, "KT1SM849krq9FFxGWCZyc7X5GvAz8XnRmXnf", 10, 0) + updates, err := s.ticketUpdates.Updates(ctx, ticket.UpdatesRequest{ + Ticketer: "KT1SM849krq9FFxGWCZyc7X5GvAz8XnRmXnf", + Limit: 10, + Offset: 0, + }) + s.Require().NoError(err) + s.Require().Len(updates, 2) + + update := updates[0] + s.Require().EqualValues(2, update.ID) + s.Require().EqualValues(104, update.OperationId) + s.Require().EqualValues(40, update.Level) + s.Require().EqualValues(1, update.TicketId) + s.Require().EqualValues(131, update.AccountId) + s.Require().EqualValues("43", update.Amount.String()) + s.Require().EqualValues("KT1SM849krq9FFxGWCZyc7X5GvAz8XnRmXnf", update.Ticket.Ticketer.Address) +} + +func (s *StorageTestSuite) TestTicketUpdatesForAccount() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + updates, err := s.ticketUpdates.Updates(ctx, ticket.UpdatesRequest{ + Account: "tz2GGf91VB3gK7MaaP8Xua6ohhHAhH4hTC11", + Limit: 10, + Offset: 0, + }) + s.Require().NoError(err) + s.Require().Len(updates, 2) + + update := updates[1] + s.Require().EqualValues(2, update.ID) + s.Require().EqualValues(104, update.OperationId) + s.Require().EqualValues(40, update.Level) + s.Require().EqualValues(1, update.TicketId) + s.Require().EqualValues(131, update.AccountId) + s.Require().EqualValues("43", update.Amount.String()) + s.Require().EqualValues("KT1SM849krq9FFxGWCZyc7X5GvAz8XnRmXnf", update.Ticket.Ticketer.Address) +} + +func (s *StorageTestSuite) TestTicketUpdatesByTicketId() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + + updates, err := s.ticketUpdates.Updates(ctx, ticket.UpdatesRequest{ + TicketId: testsuite.Ptr(uint64(1)), + Limit: 10, + Offset: 0, + }) s.Require().NoError(err) s.Require().Len(updates, 2) diff --git a/internal/postgres/ticket/storage.go b/internal/postgres/ticket/storage.go index a8386b3ac..63e315fc9 100644 --- a/internal/postgres/ticket/storage.go +++ b/internal/postgres/ticket/storage.go @@ -19,17 +19,8 @@ func NewStorage(pg *core.Postgres) *Storage { return &Storage{pg} } -// Get - -func (storage *Storage) Updates(ctx context.Context, ticketer string, limit, offset int64) (response []ticket.TicketUpdate, err error) { - var ticketerId uint64 - if err := storage.DB.NewSelect(). - Model((*account.Account)(nil)). - Column("id"). - Where("address = ?", ticketer). - Limit(1). - Scan(ctx, &ticketerId); err != nil { - return nil, err - } +// Updates - +func (storage *Storage) Updates(ctx context.Context, req ticket.UpdatesRequest) (response []ticket.TicketUpdate, err error) { query := storage.DB. NewSelect(). Model(&response). @@ -38,13 +29,40 @@ func (storage *Storage) Updates(ctx context.Context, ticketer string, limit, off Relation("Account", func(sq *bun.SelectQuery) *bun.SelectQuery { return sq.Column("address") }). - Where("ticket.ticketer_id = ?", ticketerId) + Limit(storage.GetPageSize(req.Limit)) + + if req.Ticketer != "" { + var ticketerId uint64 + if err := storage.DB.NewSelect(). + Model((*account.Account)(nil)). + Column("id"). + Where("address = ?", req.Ticketer). + Limit(1). + Scan(ctx, &ticketerId); err != nil { + return nil, err + } + query.Where("ticket.ticketer_id = ?", ticketerId) + } - if offset > 0 { - query.Offset(int(offset)) + if req.Account != "" { + var accountId uint64 + if err := storage.DB.NewSelect(). + Model((*account.Account)(nil)). + Column("id"). + Where("address = ?", req.Account). + Limit(1). + Scan(ctx, &accountId); err != nil { + return nil, err + } + query.Where("account_id = ?", accountId) + } + + if req.TicketId != nil { + query.Where("ticket_id = ?", *req.TicketId) } - if limit > 0 { - query.Limit(storage.GetPageSize(limit)) + + if req.Offset > 0 { + query.Offset(int(req.Offset)) } err = query.Order("id desc").Scan(ctx)