diff --git a/pkg/infra/usagestats/service/api.go b/pkg/infra/usagestats/service/api.go new file mode 100644 index 00000000000..be27e79c394 --- /dev/null +++ b/pkg/infra/usagestats/service/api.go @@ -0,0 +1,27 @@ +package service + +import ( + "net/http" + + "github.com/grafana/grafana/pkg/api/response" + "github.com/grafana/grafana/pkg/api/routing" + "github.com/grafana/grafana/pkg/middleware" + "github.com/grafana/grafana/pkg/models" +) + +const rootUrl = "/api/admin" + +func (uss *UsageStats) registerAPIEndpoints() { + uss.RouteRegister.Group(rootUrl, func(subrouter routing.RouteRegister) { + subrouter.Get("/usage-report-preview", middleware.ReqGrafanaAdmin, routing.Wrap(uss.getUsageReportPreview)) + }) +} + +func (uss *UsageStats) getUsageReportPreview(ctx *models.ReqContext) response.Response { + usageReport, err := uss.GetUsageReport(ctx.Req.Context()) + if err != nil { + return response.Error(http.StatusInternalServerError, "failed to get usage report", err) + } + + return response.JSON(http.StatusOK, usageReport) +} diff --git a/pkg/infra/usagestats/service/api_test.go b/pkg/infra/usagestats/service/api_test.go new file mode 100644 index 00000000000..62cf46d9d97 --- /dev/null +++ b/pkg/infra/usagestats/service/api_test.go @@ -0,0 +1,134 @@ +package service + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "path" + "testing" + + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/web" + "github.com/stretchr/testify/require" +) + +func TestApi_getUsageStats(t *testing.T) { + type getUsageStatsTestCase struct { + desc string + expectedStatus int + expectedCall bool + IsGrafanaAdmin bool + enabled bool + } + tests := []getUsageStatsTestCase{ + { + desc: "expect usage stats", + enabled: true, + IsGrafanaAdmin: true, + expectedCall: true, + expectedStatus: 200, + }, + { + desc: "expect usage stat preview still there after disabling", + enabled: false, + IsGrafanaAdmin: true, + expectedCall: true, + expectedStatus: 200, + }, + { + desc: "expect http status 403 when not admin", + enabled: false, + IsGrafanaAdmin: false, + expectedCall: false, + expectedStatus: 403, + }, + } + + uss := createService(t, setting.Cfg{}) + uss.registerAPIEndpoints() + getSystemStatsWasCalled := false + + uss.Bus.AddHandler(func(ctx context.Context, query *models.GetSystemStatsQuery) error { + query.Result = &models.SystemStats{} + getSystemStatsWasCalled = true + return nil + }) + + uss.Bus.AddHandler(func(ctx context.Context, query *models.GetDataSourceStatsQuery) error { + query.Result = []*models.DataSourceStats{} + return nil + }) + + uss.Bus.AddHandler(func(ctx context.Context, query *models.GetDataSourcesByTypeQuery) error { + query.Result = []*models.DataSource{} + return nil + }) + + uss.Bus.AddHandler(func(ctx context.Context, query *models.GetDataSourceAccessStatsQuery) error { + query.Result = []*models.DataSourceAccessStats{} + return nil + }) + + uss.Bus.AddHandler(func(ctx context.Context, query *models.GetAlertNotifierUsageStatsQuery) error { + query.Result = []*models.NotifierUsageStats{} + return nil + }) + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + getSystemStatsWasCalled = false + uss.Cfg.ReportingEnabled = tt.enabled + server := setupTestServer(t, &models.SignedInUser{OrgId: 1, IsGrafanaAdmin: tt.IsGrafanaAdmin}, uss) + + usageStats, recorder := getUsageStats(t, server) + require.Equal(t, tt.expectedCall, getSystemStatsWasCalled) + require.Equal(t, tt.expectedStatus, recorder.Code) + + if tt.expectedStatus == http.StatusOK { + require.NotNil(t, usageStats) + } + }) + } +} + +func getUsageStats(t *testing.T, server *web.Mux) (*models.SystemStats, *httptest.ResponseRecorder) { + req, err := http.NewRequest(http.MethodGet, "/api/admin/usage-report-preview", http.NoBody) + require.NoError(t, err) + recorder := httptest.NewRecorder() + server.ServeHTTP(recorder, req) + + var usageStats *models.SystemStats + if recorder.Code == http.StatusOK { + require.NoError(t, json.NewDecoder(recorder.Body).Decode(&usageStats)) + } + return usageStats, recorder +} + +func setupTestServer(t *testing.T, user *models.SignedInUser, service *UsageStats) *web.Mux { + server := web.New() + server.UseMiddleware(web.Renderer(path.Join(setting.StaticRootPath, "views"), "[[", "]]")) + server.Use(contextProvider(&testContext{user})) + service.RouteRegister.Register(server) + return server +} + +type testContext struct { + user *models.SignedInUser +} + +func contextProvider(tc *testContext) web.Handler { + return func(c *web.Context) { + signedIn := tc.user != nil + reqCtx := &models.ReqContext{ + Context: c, + SignedInUser: tc.user, + IsSignedIn: signedIn, + SkipCache: true, + Logger: log.New("test"), + } + c.Map(reqCtx) + } +} diff --git a/pkg/infra/usagestats/service/service.go b/pkg/infra/usagestats/service/service.go index e9300a83fc4..13ac15b2be1 100644 --- a/pkg/infra/usagestats/service/service.go +++ b/pkg/infra/usagestats/service/service.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/kvstore" "github.com/grafana/grafana/pkg/infra/log" @@ -22,6 +23,7 @@ type UsageStats struct { pluginStore plugins.Store SocialService social.Service kvStore *kvstore.NamespacedKVStore + RouteRegister routing.RouteRegister log log.MultiLoggers @@ -33,18 +35,22 @@ type UsageStats struct { } func ProvideService(cfg *setting.Cfg, bus bus.Bus, sqlStore *sqlstore.SQLStore, pluginStore plugins.Store, - socialService social.Service, kvStore kvstore.KVStore) *UsageStats { + socialService social.Service, kvStore kvstore.KVStore, routeRegister routing.RouteRegister, +) *UsageStats { s := &UsageStats{ Cfg: cfg, Bus: bus, SQLStore: sqlStore, oauthProviders: socialService.GetOAuthProviders(), + RouteRegister: routeRegister, pluginStore: pluginStore, kvStore: kvstore.WithNamespace(kvStore, 0, "infra.usagestats"), log: log.New("infra.usagestats"), startTime: time.Now(), } + s.registerAPIEndpoints() + return s } diff --git a/pkg/infra/usagestats/service/usage_stats_test.go b/pkg/infra/usagestats/service/usage_stats_test.go index fa9f53bae81..bbbae5c6644 100644 --- a/pkg/infra/usagestats/service/usage_stats_test.go +++ b/pkg/infra/usagestats/service/usage_stats_test.go @@ -11,6 +11,7 @@ import ( "testing" "time" + "github.com/grafana/grafana/pkg/api/routing" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/kvstore" @@ -619,5 +620,6 @@ func createService(t *testing.T, cfg setting.Cfg) *UsageStats { kvStore: kvstore.WithNamespace(kvstore.ProvideService(sqlStore), 0, "infra.usagestats"), log: log.New("infra.usagestats"), startTime: time.Now().Add(-1 * time.Minute), + RouteRegister: routing.NewRouteRegister(), } }