mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
api: Validate dashboards on save via coremodels, behind feature toggle (#48252)
* Add coremodelValidation feature flag * coremodels: use stubs when feature flag is off * api: validate dashboards on save * Need pointer receiver for FeatureManager * Update dashboard Go model * Align doc comments * Include CoremodelRegistry in test * Wedge coremodel in on all test cases, ugh * Ugh fix comment again * Update pkg/framework/coremodel/staticregistry/provide.go Co-authored-by: Artur Wierzbicki <wierzbicki.artur.94@gmail.com> * Update Thema (and its deps) for better errs * omg whitespace Co-authored-by: Artur Wierzbicki <wierzbicki.artur.94@gmail.com>
This commit is contained in:
parent
03fe1435a0
commit
a3402641d6
9
go.mod
9
go.mod
@ -103,7 +103,7 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.6.3
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29
|
||||
golang.org/x/exp v0.0.0-20210220032938-85be41e4509f
|
||||
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861 // indirect
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65
|
||||
@ -153,7 +153,7 @@ require (
|
||||
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/edsrzf/mmap-go v1.0.0 // indirect
|
||||
github.com/emicklei/proto v1.6.15 // indirect
|
||||
github.com/emicklei/proto v1.10.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.2 // indirect
|
||||
github.com/go-kit/log v0.1.0
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
@ -211,7 +211,7 @@ require (
|
||||
github.com/prometheus/exporter-toolkit v0.7.0 // indirect
|
||||
github.com/prometheus/node_exporter v1.0.0-rc.0.0.20200428091818-01054558c289 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20201118171849-f6a6b3f636fc // indirect
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b // indirect
|
||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect
|
||||
github.com/rs/cors v1.8.2 // indirect
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
||||
@ -247,7 +247,7 @@ require (
|
||||
github.com/blugelabs/bluge v0.1.9
|
||||
github.com/golang-migrate/migrate/v4 v4.7.0
|
||||
github.com/grafana/dskit v0.0.0-20211011144203-3a88ec0b675f
|
||||
github.com/grafana/thema v0.0.0-20220413232647-fc54c169b508
|
||||
github.com/grafana/thema v0.0.0-20220427204245-a557e8970249
|
||||
go.etcd.io/etcd v3.3.25+incompatible
|
||||
go.opentelemetry.io/contrib/propagators/jaeger v1.6.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.6.3
|
||||
@ -298,6 +298,7 @@ require (
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/klauspost/compress v1.15.1 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.8 // indirect
|
||||
|
14
go.sum
14
go.sum
@ -883,6 +883,8 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/proto v1.6.15 h1:XbpwxmuOPrdES97FrSfpyy67SSCV/wBIKXqgJzh6hNw=
|
||||
github.com/emicklei/proto v1.6.15/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw=
|
||||
github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@ -1461,6 +1463,8 @@ github.com/grafana/saml v0.0.0-20211007135653-aed1b2edd86b/go.mod h1:q83kyQoMD0v
|
||||
github.com/grafana/sqlds/v2 v2.3.2/go.mod h1:34uyqPBWsEvg4V/xxh6V4uIqwu1qLfOfsmScll/ukrk=
|
||||
github.com/grafana/thema v0.0.0-20220413232647-fc54c169b508 h1:6k0scTj6kRDjn/qLigsLc9OTiMMaWkRUxjKBFowWEWg=
|
||||
github.com/grafana/thema v0.0.0-20220413232647-fc54c169b508/go.mod h1:KuqTKX9lfM87uu9vt9DS/q+REqSrAm2xYMnBBvlmevA=
|
||||
github.com/grafana/thema v0.0.0-20220427204245-a557e8970249 h1:PLB1iSjrosHU5MN3eJ2tSvjXX9zkek7gLeBF/L/3oFo=
|
||||
github.com/grafana/thema v0.0.0-20220427204245-a557e8970249/go.mod h1:KuqTKX9lfM87uu9vt9DS/q+REqSrAm2xYMnBBvlmevA=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
@ -2023,6 +2027,8 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI
|
||||
github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI=
|
||||
github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
@ -2390,6 +2396,8 @@ github.com/prometheus/statsd_exporter v0.21.0/go.mod h1:rbT83sZq2V+p73lHhPZfMc3M
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20201118171849-f6a6b3f636fc h1:gSVONBi2HWMFXCa9jFdYvYk7IwW/mTLxWOF7rXS4LO0=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20201118171849-f6a6b3f636fc/go.mod h1:KbKfKPy2I6ecOIGA9apfheFv14+P3RSmmQvshofQyMY=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b h1:zd/2RNzIRkoGGMjE+YIsZ85CnDIz672JK2F3Zl4vux4=
|
||||
github.com/protocolbuffers/txtpbfmt v0.0.0-20220428173112-74888fd59c2b/go.mod h1:KjY0wibdYKc4DYkerHSbguaf3JeIPGhNJBp2BNiFH78=
|
||||
github.com/rafaeljusto/redigomock v0.0.0-20190202135759-257e089e14a1/go.mod h1:JaY6n2sDr+z2WTsXkOmNRUfDy6FN0L6Nk7x06ndm4tY=
|
||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
|
||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
|
||||
@ -2602,10 +2610,14 @@ github.com/thanos-io/thanos v0.13.1-0.20210224074000-659446cab117/go.mod h1:kdqF
|
||||
github.com/thanos-io/thanos v0.13.1-0.20210226164558-03dace0a1aa1/go.mod h1:gMCy4oCteKTT7VuXVvXLTPGzzjovX1VPE5p+HgL1hyU=
|
||||
github.com/thanos-io/thanos v0.13.1-0.20210401085038-d7dff0c84d17/go.mod h1:zU8KqE+6A+HksK4wiep8e/3UvCZLm+Wrw9AqZGaAm9k=
|
||||
github.com/thanos-io/thanos v0.22.0/go.mod h1:SZDWz3phcUcBr4MYFoPFRvl+Z9Nbi45HlwQlwSZSt+Q=
|
||||
github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
|
||||
github.com/tidwall/gjson v1.6.1/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0=
|
||||
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
|
||||
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/tinylru v1.0.2/go.mod h1:HDVL7TsWeezQ4g44Um84TOVBMFcq7Xa9giqNc805KJ8=
|
||||
@ -3120,6 +3132,8 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
||||
golang.org/x/net v0.0.0-20220401154927-543a649e0bdd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861 h1:yssD99+7tqHWO5Gwh81phT+67hg+KttniBr6UnEXOY8=
|
||||
golang.org/x/net v0.0.0-20220421235706-1d1ef9303861/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -59,4 +59,5 @@ export interface FeatureToggles {
|
||||
azureMonitorExperimentalUI?: boolean;
|
||||
traceToMetrics?: boolean;
|
||||
prometheusStreamingJSONParser?: boolean;
|
||||
validateDashboardsOnSave?: boolean;
|
||||
}
|
||||
|
@ -16,11 +16,14 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/components/dashdiffs"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/coremodel/dashboard"
|
||||
"github.com/grafana/grafana/pkg/cuectx"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||
"github.com/grafana/grafana/pkg/services/star"
|
||||
@ -305,6 +308,28 @@ func (hs *HTTPServer) PostDashboard(c *models.ReqContext) response.Response {
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
|
||||
if hs.Features.IsEnabled(featuremgmt.FlagValidateDashboardsOnSave) {
|
||||
// Ideally, coremodel validation calls would be integrated into the web
|
||||
// framework. But this does the job for now.
|
||||
if cm, has := hs.CoremodelRegistry.Get("dashboard"); has {
|
||||
schv, err := cmd.Dashboard.Get("schemaVersion").Int()
|
||||
|
||||
// Only try to validate if the schemaVersion is at least the handoff version
|
||||
// (the minimum schemaVersion against which the dashboard schema is known to
|
||||
// work), or if schemaVersion is absent (which will happen once the Thema
|
||||
// schema becomes canonical).
|
||||
if err != nil || schv >= dashboard.HandoffSchemaVersion {
|
||||
// Can't fail, web.Bind() already ensured it's valid JSON
|
||||
b, _ := cmd.Dashboard.Bytes()
|
||||
v, _ := cuectx.JSONtoCUE("dashboard.json", b)
|
||||
if _, err := cm.CurrentSchema().Validate(v); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "invalid dashboard json", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hs.postDashboard(c, cmd)
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,9 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/coremodel/dashboard"
|
||||
"github.com/grafana/grafana/pkg/cuectx"
|
||||
"github.com/grafana/grafana/pkg/framework/coremodel"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -23,7 +26,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
service "github.com/grafana/grafana/pkg/services/dashboards/service"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/service"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/libraryelements"
|
||||
@ -51,6 +54,7 @@ func TestGetHomeDashboard(t *testing.T) {
|
||||
Cfg: cfg,
|
||||
pluginStore: &fakePluginStore{},
|
||||
SQLStore: mockstore.NewSQLStoreMock(),
|
||||
CoremodelRegistry: setupDashboardCoremodel(t),
|
||||
preferenceService: prefService,
|
||||
}
|
||||
|
||||
@ -127,12 +131,13 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
mockSQLStore := mockstore.NewSQLStoreMock()
|
||||
|
||||
hs := &HTTPServer{
|
||||
Cfg: setting.NewCfg(),
|
||||
pluginStore: &fakePluginStore{},
|
||||
SQLStore: mockSQLStore,
|
||||
AccessControl: accesscontrolmock.New(),
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
dashboardService: dashboardService,
|
||||
Cfg: setting.NewCfg(),
|
||||
pluginStore: &fakePluginStore{},
|
||||
SQLStore: mockSQLStore,
|
||||
CoremodelRegistry: setupDashboardCoremodel(t),
|
||||
AccessControl: accesscontrolmock.New(),
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
dashboardService: dashboardService,
|
||||
}
|
||||
|
||||
setUp := func() {
|
||||
@ -235,6 +240,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
Live: newTestLive(t, sql),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
CoremodelRegistry: setupDashboardCoremodel(t),
|
||||
SQLStore: mockSQLStore,
|
||||
AccessControl: accesscontrolmock.New(),
|
||||
dashboardService: dashboardService,
|
||||
@ -914,6 +920,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
dashboardProvisioningService: mockDashboardProvisioningService{},
|
||||
CoremodelRegistry: setupDashboardCoremodel(t),
|
||||
SQLStore: mockSQLStore,
|
||||
AccessControl: accesscontrolmock.New(),
|
||||
dashboardService: dashboardService,
|
||||
@ -958,6 +965,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
|
||||
LibraryElementService: &libraryElementsService,
|
||||
SQLStore: sc.sqlStore,
|
||||
ProvisioningService: provisioningService,
|
||||
CoremodelRegistry: setupDashboardCoremodel(t),
|
||||
AccessControl: accesscontrolmock.New(),
|
||||
dashboardProvisioningService: service.ProvideDashboardService(
|
||||
cfg, dashboardStore, nil, features,
|
||||
@ -1026,6 +1034,7 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
|
||||
pluginStore: &fakePluginStore{},
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
CoremodelRegistry: setupDashboardCoremodel(t),
|
||||
dashboardService: dashboardService,
|
||||
folderService: folderService,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
@ -1057,6 +1066,7 @@ func postDiffScenario(t *testing.T, desc string, url string, routePattern string
|
||||
QuotaService: "a.QuotaService{Cfg: cfg},
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
CoremodelRegistry: setupDashboardCoremodel(t),
|
||||
SQLStore: sqlmock,
|
||||
}
|
||||
|
||||
@ -1090,6 +1100,7 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
||||
QuotaService: "a.QuotaService{Cfg: cfg},
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
CoremodelRegistry: setupDashboardCoremodel(t),
|
||||
dashboardService: mock,
|
||||
SQLStore: sqlStore,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
@ -1116,6 +1127,16 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
|
||||
})
|
||||
}
|
||||
|
||||
func setupDashboardCoremodel(t *testing.T) *coremodel.Registry {
|
||||
// TODO abstract and generalize this further for wider reuse
|
||||
t.Helper()
|
||||
dcm, err := dashboard.ProvideCoremodel(cuectx.ProvideThemaLibrary())
|
||||
require.NoError(t, err)
|
||||
reg, err := coremodel.NewRegistry(dcm)
|
||||
require.NoError(t, err)
|
||||
return reg
|
||||
}
|
||||
|
||||
func (sc *scenarioContext) ToJSON() *simplejson.Json {
|
||||
result := simplejson.New()
|
||||
err := json.NewDecoder(sc.resp.Body).Decode(result)
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
httpstatic "github.com/grafana/grafana/pkg/api/static"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/framework/coremodel"
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
@ -154,6 +155,7 @@ type HTTPServer struct {
|
||||
folderPermissionsService accesscontrol.FolderPermissionsService
|
||||
dashboardPermissionsService accesscontrol.DashboardPermissionsService
|
||||
starService star.Service
|
||||
CoremodelRegistry *coremodel.Registry
|
||||
}
|
||||
|
||||
type ServerOptions struct {
|
||||
@ -187,7 +189,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
dashboardsnapshotsService *dashboardsnapshots.Service, commentsService *comments.Service, pluginSettings *pluginSettings.Service,
|
||||
avatarCacheServer *avatar.AvatarCacheServer, preferenceService pref.Service, entityEventsService store.EntityEventsService,
|
||||
teamsPermissionsService accesscontrol.TeamPermissionsService, folderPermissionsService accesscontrol.FolderPermissionsService,
|
||||
dashboardPermissionsService accesscontrol.DashboardPermissionsService, starService star.Service,
|
||||
dashboardPermissionsService accesscontrol.DashboardPermissionsService, starService star.Service, coremodelRegistry *coremodel.Registry,
|
||||
) (*HTTPServer, error) {
|
||||
web.Env = cfg.Env
|
||||
m := web.New()
|
||||
@ -265,6 +267,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
folderPermissionsService: folderPermissionsService,
|
||||
dashboardPermissionsService: dashboardPermissionsService,
|
||||
starService: starService,
|
||||
CoremodelRegistry: coremodelRegistry,
|
||||
}
|
||||
if hs.Listener != nil {
|
||||
hs.log.Debug("Using provided listener")
|
||||
|
@ -92,9 +92,19 @@ type model struct {
|
||||
Refresh interface{} `json:"refresh"` // (bool|string)
|
||||
SchemaVersion int `json:"schemaVersion"`
|
||||
Links []struct {
|
||||
Title string `json:"title"`
|
||||
Type string `json:"type"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Tooltip string `json:"tooltip,omitempty"`
|
||||
Url string `json:"url,omitempty"`
|
||||
Tags []string `json:"tags"`
|
||||
AsDropdown bool `json:"asDropdown"`
|
||||
TargetBlank bool `json:"targetBlank"`
|
||||
IncludeVars bool `json:"includeVars"`
|
||||
KeepTime bool `json:"keepTime"`
|
||||
} `json:"links"`
|
||||
Panels []interface{} `json:"panels"`
|
||||
FiscalYearStartMonth string `json:"fiscalYearStartMonth"`
|
||||
FiscalYearStartMonth uint8 `json:"fiscalYearStartMonth"`
|
||||
LiveNow bool `json:"liveNow"`
|
||||
WeekStart string `json:"weekStart"`
|
||||
|
||||
|
@ -76,3 +76,12 @@ func (r *Registry) addModels(models []Interface) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves a coremodel with the given string identifier. nil, false
|
||||
// is returned if no such coremodel exists.
|
||||
func (r *Registry) Get(name string) (cm Interface, has bool) {
|
||||
r.lock.RLock()
|
||||
cm, has = r.modelIdx[name]
|
||||
r.lock.RUnlock()
|
||||
return
|
||||
}
|
||||
|
@ -11,7 +11,9 @@ import (
|
||||
func ProvideRegistry(
|
||||
dashboard *dashboard.Coremodel,
|
||||
) (*coremodel.Registry, error) {
|
||||
return coremodel.NewRegistry(
|
||||
cmlist := []coremodel.Interface{
|
||||
dashboard,
|
||||
)
|
||||
}
|
||||
|
||||
return coremodel.NewRegistry(cmlist...)
|
||||
}
|
||||
|
@ -243,5 +243,11 @@ var (
|
||||
Description: "Enable streaming JSON parser for Prometheus datasource",
|
||||
State: FeatureStateAlpha,
|
||||
},
|
||||
{
|
||||
Name: "validateDashboardsOnSave",
|
||||
Description: "Validate dashboard JSON POSTed to api/dashboards/db",
|
||||
State: FeatureStateAlpha,
|
||||
RequiresRestart: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@ -178,4 +178,8 @@ const (
|
||||
// FlagPrometheusStreamingJSONParser
|
||||
// Enable streaming JSON parser for Prometheus datasource
|
||||
FlagPrometheusStreamingJSONParser = "prometheusStreamingJSONParser"
|
||||
|
||||
// FlagValidateDashboardsOnSave
|
||||
// Validate dashboard JSON POSTed to api/dashboards/db
|
||||
FlagValidateDashboardsOnSave = "validateDashboardsOnSave"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user