Add usage stats preview endpoint (#43899)

* Stats: add preview route for usage statistics

* Stats: respect reporting settings

* Stats: add tests to api endpoint

* Stats: unregister route

* Stats: always possible to preview stats

* Update pkg/infra/usagestats/service/api.go

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>

Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com>
This commit is contained in:
J Guerreiro 2022-01-14 09:16:07 +00:00 committed by GitHub
parent bedc5cccaa
commit 1e9818a69f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 170 additions and 1 deletions

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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(),
}
}