diff --git a/cmd/api/docs/swagger.json b/cmd/api/docs/swagger.json index 4b99ff21..ebd55cc3 100644 --- a/cmd/api/docs/swagger.json +++ b/cmd/api/docs/swagger.json @@ -3135,6 +3135,36 @@ } } }, + "/stats/messages_count_24h": { + "get": { + "description": "Get messages distribution for the last 24 hours", + "produces": [ + "application/json" + ], + "tags": [ + "stats" + ], + "summary": "Get messages distribution for the last 24 hours", + "operationId": "stats-messages-count-24h", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/responses.CountItem" + } + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/handler.Error" + } + } + } + } + }, "/stats/namespace/series/{id}/{name}/{timeframe}": { "get": { "description": "Get histogram for namespace with precomputed stats by series name and timeframe", @@ -5207,6 +5237,21 @@ } } }, + "responses.CountItem": { + "type": "object", + "properties": { + "name": { + "type": "string", + "format": "string", + "example": "test" + }, + "value": { + "type": "string", + "format": "string", + "example": "17632" + } + } + }, "responses.Delegation": { "type": "object", "properties": { diff --git a/cmd/api/handler/responses/stats.go b/cmd/api/handler/responses/stats.go index 62f25740..9a8dfd36 100644 --- a/cmd/api/handler/responses/stats.go +++ b/cmd/api/handler/responses/stats.go @@ -151,3 +151,15 @@ func NewRollupStats24h(stats storage.RollupStats24h) RollupStats24h { BlobsCount: stats.BlobsCount, } } + +type CountItem struct { + Name string `example:"test" format:"string" json:"name" swaggertype:"string"` + Value int64 `example:"17632" format:"string" json:"value" swaggertype:"string"` +} + +func NewCountItem(item storage.CountItem) CountItem { + return CountItem{ + Name: item.Name, + Value: item.Value, + } +} diff --git a/cmd/api/handler/stats.go b/cmd/api/handler/stats.go index 770dedc3..af05e10d 100644 --- a/cmd/api/handler/stats.go +++ b/cmd/api/handler/stats.go @@ -519,3 +519,28 @@ func (sh StatsHandler) RollupStats24h(c echo.Context) error { } return returnArray(c, response) } + +// MessagesCount24h godoc +// +// @Summary Get messages distribution for the last 24 hours +// @Description Get messages distribution for the last 24 hours +// @Tags stats +// @ID stats-messages-count-24h +// @Produce json +// @Success 200 {array} responses.CountItem +// @Failure 500 {object} Error +// @Router /stats/messages_count_24h [get] +func (sh StatsHandler) MessagesCount24h(c echo.Context) error { + items, err := sh.repo.MessagesCount24h( + c.Request().Context(), + ) + if err != nil { + return handleError(c, err, sh.nsRepo) + } + + response := make([]responses.CountItem, len(items)) + for i := range items { + response[i] = responses.NewCountItem(items[i]) + } + return returnArray(c, response) +} diff --git a/cmd/api/handler/stats_test.go b/cmd/api/handler/stats_test.go index 16add19e..2f311e24 100644 --- a/cmd/api/handler/stats_test.go +++ b/cmd/api/handler/stats_test.go @@ -555,3 +555,30 @@ func (s *StatsTestSuite) TestRollupStats24h() { s.Require().EqualValues("name", item.Name) s.Require().EqualValues("logo", item.Logo) } + +func (s *StatsTestSuite) TestMessgaesCount24h() { + req := httptest.NewRequest(http.MethodGet, "/", nil) + rec := httptest.NewRecorder() + c := s.echo.NewContext(req, rec) + c.SetPath("/v1/stats/messages_count_24h") + + s.stats.EXPECT(). + MessagesCount24h(gomock.Any()). + Return([]storage.CountItem{ + { + Name: "test", + Value: 100, + }, + }, nil) + + s.Require().NoError(s.handler.MessagesCount24h(c)) + s.Require().Equal(http.StatusOK, rec.Code) + + var response []responses.CountItem + err := json.NewDecoder(rec.Body).Decode(&response) + s.Require().NoError(err) + s.Require().Len(response, 1) + + s.Require().EqualValues("test", response[0].Name) + s.Require().EqualValues(100, response[0].Value) +} diff --git a/cmd/api/init.go b/cmd/api/init.go index 378c909a..bb1dc2a4 100644 --- a/cmd/api/init.go +++ b/cmd/api/init.go @@ -393,6 +393,7 @@ func initHandlers(ctx context.Context, e *echo.Echo, cfg Config, db postgres.Sto stats.GET("/changes_24h", statsHandler.Change24hBlockStats) stats.GET("/rollup_stats_24h", statsHandler.RollupStats24h) stats.GET("/square_size", statsHandler.SquareSize) + stats.GET("/messages_count_24h", statsHandler.MessagesCount24h) price := stats.Group("/price") { diff --git a/cmd/api/routes_test.go b/cmd/api/routes_test.go index f016a57c..c2ce98a6 100644 --- a/cmd/api/routes_test.go +++ b/cmd/api/routes_test.go @@ -99,6 +99,7 @@ func TestRoutes(t *testing.T) { "/v1/docs GET": {}, "/v1/stats/square_size GET": {}, "/v1/stats/rollup_stats_24h GET": {}, + "/v1/stats/messages_count_24h GET": {}, } ctx, cancel := context.WithCancel(context.Background()) diff --git a/internal/storage/mock/stats.go b/internal/storage/mock/stats.go index 0221685a..6d8096e9 100644 --- a/internal/storage/mock/stats.go +++ b/internal/storage/mock/stats.go @@ -160,6 +160,45 @@ func (c *IStatsCumulativeSeriesCall) DoAndReturn(f func(context.Context, storage return c } +// MessagesCount24h mocks base method. +func (m *MockIStats) MessagesCount24h(ctx context.Context) ([]storage.CountItem, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "MessagesCount24h", ctx) + ret0, _ := ret[0].([]storage.CountItem) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// MessagesCount24h indicates an expected call of MessagesCount24h. +func (mr *MockIStatsMockRecorder) MessagesCount24h(ctx any) *IStatsMessagesCount24hCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MessagesCount24h", reflect.TypeOf((*MockIStats)(nil).MessagesCount24h), ctx) + return &IStatsMessagesCount24hCall{Call: call} +} + +// IStatsMessagesCount24hCall wrap *gomock.Call +type IStatsMessagesCount24hCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *IStatsMessagesCount24hCall) Return(arg0 []storage.CountItem, arg1 error) *IStatsMessagesCount24hCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *IStatsMessagesCount24hCall) Do(f func(context.Context) ([]storage.CountItem, error)) *IStatsMessagesCount24hCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *IStatsMessagesCount24hCall) DoAndReturn(f func(context.Context) ([]storage.CountItem, error)) *IStatsMessagesCount24hCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + // NamespaceSeries mocks base method. func (m *MockIStats) NamespaceSeries(ctx context.Context, timeframe storage.Timeframe, name string, nsId uint64, req storage.SeriesRequest) ([]storage.SeriesItem, error) { m.ctrl.T.Helper() diff --git a/internal/storage/postgres/stats.go b/internal/storage/postgres/stats.go index d19b3d4b..70964ebf 100644 --- a/internal/storage/postgres/stats.go +++ b/internal/storage/postgres/stats.go @@ -397,6 +397,16 @@ func (s Stats) RollupStats24h(ctx context.Context) (response []storage.RollupSta Join("left join rollup as r on r.id = grouped.rollup_id"). Order("blobs_count desc"). Scan(ctx, &response) + return +} +func (s Stats) MessagesCount24h(ctx context.Context) (response []storage.CountItem, err error) { + err = s.db.DB().NewSelect(). + Model((*storage.Message)(nil)). + ColumnExpr("count(*) as value, type as name"). + Where("time > now() - '1 day'::interval"). + Group("type"). + Order("value desc"). + Scan(ctx, &response) return } diff --git a/internal/storage/postgres/stats_test.go b/internal/storage/postgres/stats_test.go index 63c14acc..f18dbcf3 100644 --- a/internal/storage/postgres/stats_test.go +++ b/internal/storage/postgres/stats_test.go @@ -296,6 +296,15 @@ func (s *StatsTestSuite) TestRollupStats24h() { s.Require().Len(stats, 0) } +func (s *StatsTestSuite) TestMessagesCount24h() { + ctx, ctxCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer ctxCancel() + + items, err := s.storage.Stats.MessagesCount24h(ctx) + s.Require().NoError(err) + s.Require().Len(items, 0) +} + func TestSuiteStats_Run(t *testing.T) { suite.Run(t, new(StatsTestSuite)) } diff --git a/internal/storage/stats.go b/internal/storage/stats.go index 02c8cb50..b35846ec 100644 --- a/internal/storage/stats.go +++ b/internal/storage/stats.go @@ -112,6 +112,11 @@ type DistributionItem struct { Value string `bun:"value"` } +type CountItem struct { + Name string `bun:"name"` + Value int64 `bun:"value"` +} + func NewSeriesRequest(from, to int64) SeriesRequest { var seriesRequest SeriesRequest if from > 0 { @@ -174,4 +179,5 @@ type IStats interface { RollupStats24h(ctx context.Context) ([]RollupStats24h, error) SquareSize(ctx context.Context, from, to *time.Time) (map[int][]SeriesItem, error) Change24hBlockStats(ctx context.Context) (response Change24hBlockStats, err error) + MessagesCount24h(ctx context.Context) ([]CountItem, error) }