Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2386d78193 | ||
|
|
fac483c393 | ||
|
|
fd67ab151d | ||
|
|
27a6d700f8 | ||
|
|
fcd658359c | ||
|
|
3d99babd0f | ||
|
|
1ffc7860e6 | ||
|
|
955b931756 | ||
|
|
afb449e8a1 | ||
|
|
3bf22fcb21 | ||
|
|
8ad6df8266 | ||
|
|
a6370342b0 |
10
.drone.yml
10
.drone.yml
@@ -3238,7 +3238,9 @@ steps:
|
||||
name: mysql-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://redis:6379/0 -timeout 120s
|
||||
- ./bin/grabpl integration-tests
|
||||
- go clean -testcache
|
||||
- go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic
|
||||
-timeout=5m {}'
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
@@ -5935,7 +5937,9 @@ steps:
|
||||
name: mysql-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://redis:6379/0 -timeout 120s
|
||||
- ./bin/grabpl integration-tests
|
||||
- go clean -testcache
|
||||
- go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic
|
||||
-timeout=5m {}'
|
||||
depends_on:
|
||||
- wire-install
|
||||
environment:
|
||||
@@ -6549,6 +6553,6 @@ kind: secret
|
||||
name: aws_secret_access_key
|
||||
---
|
||||
kind: signature
|
||||
hmac: a9051a8245848db0d2bd763f1f80d4103e093053093cf370792c18855d745602
|
||||
hmac: 934e2dbe22f80a17ebb301d68fdac9d91544dbb92486f798300bb39436405bff
|
||||
|
||||
...
|
||||
|
||||
4
go.mod
4
go.mod
@@ -126,7 +126,7 @@ require (
|
||||
gopkg.in/square/go-jose.v2 v2.5.1
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
xorm.io/builder v0.3.6 // indirect
|
||||
xorm.io/builder v0.3.6
|
||||
xorm.io/core v0.7.3
|
||||
xorm.io/xorm v0.8.2
|
||||
)
|
||||
@@ -408,7 +408,7 @@ require (
|
||||
)
|
||||
|
||||
// Use fork of crewjam/saml with fixes for some issues until changes get merged into upstream
|
||||
replace github.com/crewjam/saml => github.com/grafana/saml v0.4.13-0.20230123091136-3b6b1ec6c3cb
|
||||
replace github.com/crewjam/saml => github.com/grafana/saml v0.4.13-0.20230203140620-5f476db5c00a
|
||||
|
||||
// Thema's thema CLI requires cobra, which eventually works its way down to go-hclog@v1.0.0.
|
||||
// Upgrading affects backend plugins: https://github.com/grafana/grafana/pull/47653#discussion_r850508593
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1418,8 +1418,8 @@ github.com/grafana/phlare/api v0.1.2 h1:1jrwd3KnsXMzj/tJih9likx5EvbY3pbvLbDqAAYe
|
||||
github.com/grafana/phlare/api v0.1.2/go.mod h1:29vcLwFDmZBDce2jwFIMtzvof7fzPadT8VMKw9ks7FU=
|
||||
github.com/grafana/prometheus-alertmanager v0.25.1-0.20230119183635-ec19b0a443b7 h1:ma1CfisUaAXQzL24tCao9yMleZYsFJ853m2l0rgahyE=
|
||||
github.com/grafana/prometheus-alertmanager v0.25.1-0.20230119183635-ec19b0a443b7/go.mod h1:MnBfDPXJqXmmfPwQlCLvVUdqfnvrAw+hSPtDeaaFwj4=
|
||||
github.com/grafana/saml v0.4.13-0.20230123091136-3b6b1ec6c3cb h1:9PLj02xp4DeLTM2+ZyBMcN1sh0ir8GuF/1xXKyF+yws=
|
||||
github.com/grafana/saml v0.4.13-0.20230123091136-3b6b1ec6c3cb/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA=
|
||||
github.com/grafana/saml v0.4.13-0.20230203140620-5f476db5c00a h1:aWSTt/pTOI4uGY9DhBMG1l0GOnGjIYtaqxzYR3/q82o=
|
||||
github.com/grafana/saml v0.4.13-0.20230203140620-5f476db5c00a/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA=
|
||||
github.com/grafana/sqlds/v2 v2.3.10 h1:HWKhE0vR6LoEiE+Is8CSZOgaB//D1yqb2ntkass9Fd4=
|
||||
github.com/grafana/sqlds/v2 v2.3.10/go.mod h1:c6ibxnxRVGxV/0YkEgvy7QpQH/lyifFyV7K/14xvdIs=
|
||||
github.com/grafana/thema v0.0.0-20230122235053-b4b6714dd1c9 h1:nAdsZkvPYNH6wDPkAi9JaDSIf5i2iVz4+Rqk4AOt6sE=
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "9.4.0"
|
||||
"version": "9.4.1"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"license": "AGPL-3.0-only",
|
||||
"private": true,
|
||||
"name": "grafana",
|
||||
"version": "9.4.0",
|
||||
"version": "9.4.1",
|
||||
"repository": "github:grafana/grafana",
|
||||
"scripts": {
|
||||
"build": "yarn i18n:compile && NODE_ENV=production webpack --progress --config scripts/webpack/webpack.prod.js",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/data",
|
||||
"version": "9.4.0",
|
||||
"version": "9.4.1",
|
||||
"description": "Grafana Data Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
@@ -36,7 +36,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "6.0.1",
|
||||
"@grafana/schema": "9.4.0",
|
||||
"@grafana/schema": "9.4.1",
|
||||
"@types/d3-interpolate": "^3.0.0",
|
||||
"d3-interpolate": "3.0.1",
|
||||
"date-fns": "2.29.3",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e-selectors",
|
||||
"version": "9.4.0",
|
||||
"version": "9.4.1",
|
||||
"description": "Grafana End-to-End Test Selectors Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e",
|
||||
"version": "9.4.0",
|
||||
"version": "9.4.1",
|
||||
"description": "Grafana End-to-End Test Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
@@ -63,7 +63,7 @@
|
||||
"@babel/core": "7.20.5",
|
||||
"@babel/preset-env": "7.20.2",
|
||||
"@cypress/webpack-preprocessor": "5.16.0",
|
||||
"@grafana/e2e-selectors": "9.4.0",
|
||||
"@grafana/e2e-selectors": "9.4.1",
|
||||
"@grafana/tsconfig": "^1.2.0-rc1",
|
||||
"@mochajs/json-file-reporter": "^1.2.0",
|
||||
"babel-loader": "9.1.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@grafana/eslint-plugin",
|
||||
"description": "ESLint rules for use within the Grafana repo. Not suitable (or supported) for external use.",
|
||||
"version": "9.4.0",
|
||||
"version": "9.4.1",
|
||||
"main": "./index.cjs",
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/runtime",
|
||||
"version": "9.4.0",
|
||||
"version": "9.4.1",
|
||||
"description": "Grafana Runtime Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -37,10 +37,10 @@
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/data": "9.4.0",
|
||||
"@grafana/e2e-selectors": "9.4.0",
|
||||
"@grafana/data": "9.4.1",
|
||||
"@grafana/e2e-selectors": "9.4.1",
|
||||
"@grafana/faro-web-sdk": "1.0.0-beta2",
|
||||
"@grafana/ui": "9.4.0",
|
||||
"@grafana/ui": "9.4.1",
|
||||
"@sentry/browser": "6.19.7",
|
||||
"history": "4.10.1",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/schema",
|
||||
"version": "9.4.0",
|
||||
"version": "9.4.1",
|
||||
"description": "Grafana Schema Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/toolkit",
|
||||
"version": "9.4.0",
|
||||
"version": "9.4.1",
|
||||
"description": "Grafana Toolkit",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -51,10 +51,10 @@
|
||||
"@babel/preset-env": "7.18.9",
|
||||
"@babel/preset-react": "7.18.6",
|
||||
"@babel/preset-typescript": "7.18.6",
|
||||
"@grafana/data": "9.4.0",
|
||||
"@grafana/data": "9.4.1",
|
||||
"@grafana/eslint-config": "5.0.0",
|
||||
"@grafana/tsconfig": "^1.2.0-rc1",
|
||||
"@grafana/ui": "9.4.0",
|
||||
"@grafana/ui": "9.4.1",
|
||||
"@jest/core": "27.5.1",
|
||||
"@types/command-exists": "^1.2.0",
|
||||
"@types/eslint": "8.4.1",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/ui",
|
||||
"version": "9.4.0",
|
||||
"version": "9.4.1",
|
||||
"description": "Grafana Components Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -49,9 +49,9 @@
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.10.5",
|
||||
"@emotion/react": "11.10.5",
|
||||
"@grafana/data": "9.4.0",
|
||||
"@grafana/e2e-selectors": "9.4.0",
|
||||
"@grafana/schema": "9.4.0",
|
||||
"@grafana/data": "9.4.1",
|
||||
"@grafana/e2e-selectors": "9.4.1",
|
||||
"@grafana/schema": "9.4.1",
|
||||
"@leeoniya/ufuzzy": "0.9.0",
|
||||
"@monaco-editor/react": "4.4.6",
|
||||
"@popperjs/core": "2.11.6",
|
||||
|
||||
@@ -739,9 +739,10 @@ func (hs *HTTPServer) GetDashboardVersion(c *contextmodel.ReqContext) response.R
|
||||
|
||||
version, _ := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 32)
|
||||
query := dashver.GetDashboardVersionQuery{
|
||||
OrgID: c.OrgID,
|
||||
DashboardID: dash.ID,
|
||||
Version: int(version),
|
||||
OrgID: c.OrgID,
|
||||
DashboardID: dash.ID,
|
||||
DashboardUID: dash.UID,
|
||||
Version: int(version),
|
||||
}
|
||||
|
||||
res, err := hs.dashboardVersionService.Get(c.Req.Context(), &query)
|
||||
@@ -757,7 +758,7 @@ func (hs *HTTPServer) GetDashboardVersion(c *contextmodel.ReqContext) response.R
|
||||
dashVersionMeta := &dashver.DashboardVersionMeta{
|
||||
ID: res.ID,
|
||||
DashboardID: res.DashboardID,
|
||||
DashboardUID: dashUID,
|
||||
DashboardUID: dash.UID,
|
||||
Data: res.Data,
|
||||
ParentVersion: res.ParentVersion,
|
||||
RestoredFrom: res.RestoredFrom,
|
||||
@@ -989,7 +990,7 @@ func (hs *HTTPServer) RestoreDashboardVersion(c *contextmodel.ReqContext) respon
|
||||
return dashboardGuardianResponse(err)
|
||||
}
|
||||
|
||||
versionQuery := dashver.GetDashboardVersionQuery{DashboardID: dashID, Version: apiCmd.Version, OrgID: c.OrgID}
|
||||
versionQuery := dashver.GetDashboardVersionQuery{DashboardID: dashID, DashboardUID: dash.UID, Version: apiCmd.Version, OrgID: c.OrgID}
|
||||
version, err := hs.dashboardVersionService.Get(c.Req.Context(), &versionQuery)
|
||||
if err != nil {
|
||||
return response.Error(404, "Dashboard version not found", nil)
|
||||
|
||||
@@ -5,9 +5,8 @@ package dashboards
|
||||
import (
|
||||
context "context"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
folder "github.com/grafana/grafana/pkg/services/folder"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// FakeDashboardService is an autogenerated mock type for the DashboardService type
|
||||
|
||||
@@ -5,10 +5,11 @@ package dashboards
|
||||
import (
|
||||
context "context"
|
||||
|
||||
folder "github.com/grafana/grafana/pkg/services/folder"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
models "github.com/grafana/grafana/pkg/services/alerting/models"
|
||||
folder "github.com/grafana/grafana/pkg/services/folder"
|
||||
|
||||
quota "github.com/grafana/grafana/pkg/services/quota"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,8 +2,11 @@ package dashverimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
@@ -14,27 +17,49 @@ const (
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
store store
|
||||
store store
|
||||
dashSvc dashboards.DashboardService
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func ProvideService(db db.DB) dashver.Service {
|
||||
func ProvideService(db db.DB, dashboardService dashboards.DashboardService) dashver.Service {
|
||||
return &Service{
|
||||
store: &sqlStore{
|
||||
db: db,
|
||||
dialect: db.GetDialect(),
|
||||
},
|
||||
dashSvc: dashboardService,
|
||||
log: log.New("dashboard-version"),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Get(ctx context.Context, query *dashver.GetDashboardVersionQuery) (*dashver.DashboardVersionDTO, error) {
|
||||
// Get the DashboardUID if not populated
|
||||
if query.DashboardUID == "" {
|
||||
u, err := s.getDashUIDMaybeEmpty(ctx, query.DashboardID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query.DashboardUID = u
|
||||
}
|
||||
|
||||
// The store methods require the dashboard ID (uid is not in the dashboard
|
||||
// versions table, at time of this writing), so get the DashboardID if it
|
||||
// was not populated.
|
||||
if query.DashboardID == 0 {
|
||||
id, err := s.getDashIDMaybeEmpty(ctx, query.DashboardUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query.DashboardID = id
|
||||
}
|
||||
|
||||
version, err := s.store.Get(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
version.Data.Set("id", version.DashboardID)
|
||||
|
||||
// FIXME: the next PR will add the dashboardService so we can grab the DashboardUID
|
||||
return version.ToDTO(""), nil
|
||||
return version.ToDTO(query.DashboardUID), nil
|
||||
}
|
||||
|
||||
func (s *Service) DeleteExpired(ctx context.Context, cmd *dashver.DeleteExpiredVersionsCommand) error {
|
||||
@@ -69,6 +94,25 @@ func (s *Service) DeleteExpired(ctx context.Context, cmd *dashver.DeleteExpiredV
|
||||
|
||||
// List all dashboard versions for the given dashboard ID.
|
||||
func (s *Service) List(ctx context.Context, query *dashver.ListDashboardVersionsQuery) ([]*dashver.DashboardVersionDTO, error) {
|
||||
// Get the DashboardUID if not populated
|
||||
if query.DashboardUID == "" {
|
||||
u, err := s.getDashUIDMaybeEmpty(ctx, query.DashboardID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query.DashboardUID = u
|
||||
}
|
||||
|
||||
// The store methods require the dashboard ID (uid is not in the dashboard
|
||||
// versions table, at time of this writing), so get the DashboardID if it
|
||||
// was not populated.
|
||||
if query.DashboardID == 0 {
|
||||
id, err := s.getDashIDMaybeEmpty(ctx, query.DashboardUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query.DashboardID = id
|
||||
}
|
||||
if query.Limit == 0 {
|
||||
query.Limit = 1000
|
||||
}
|
||||
@@ -78,8 +122,42 @@ func (s *Service) List(ctx context.Context, query *dashver.ListDashboardVersions
|
||||
}
|
||||
dtos := make([]*dashver.DashboardVersionDTO, len(dvs))
|
||||
for i, v := range dvs {
|
||||
// FIXME: the next PR will add the dashboardService so we can grab the DashboardUID
|
||||
dtos[i] = v.ToDTO("")
|
||||
dtos[i] = v.ToDTO(query.DashboardUID)
|
||||
}
|
||||
return dtos, nil
|
||||
}
|
||||
|
||||
// getDashUIDMaybeEmpty is a helper function which takes a dashboardID and
|
||||
// returns the UID. If the dashboard is not found, it will return an empty
|
||||
// string.
|
||||
func (s *Service) getDashUIDMaybeEmpty(ctx context.Context, id int64) (string, error) {
|
||||
q := dashboards.GetDashboardRefByIDQuery{ID: id}
|
||||
result, err := s.dashSvc.GetDashboardUIDByID(ctx, &q)
|
||||
if err != nil {
|
||||
if errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||
s.log.Debug("dashboard not found")
|
||||
return "", nil
|
||||
} else {
|
||||
s.log.Error("error getting dashboard", err)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return result.UID, nil
|
||||
}
|
||||
|
||||
// getDashIDMaybeEmpty is a helper function which takes a dashboardUID and
|
||||
// returns the ID. If the dashboard is not found, it will return -1.
|
||||
func (s *Service) getDashIDMaybeEmpty(ctx context.Context, uid string) (int64, error) {
|
||||
q := dashboards.GetDashboardQuery{UID: uid}
|
||||
result, err := s.dashSvc.GetDashboard(ctx, &q)
|
||||
if err != nil {
|
||||
if errors.Is(err, dashboards.ErrDashboardNotFound) {
|
||||
s.log.Debug("dashboard not found")
|
||||
return -1, nil
|
||||
} else {
|
||||
s.log.Error("error getting dashboard", err)
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
return result.ID, nil
|
||||
}
|
||||
|
||||
@@ -5,16 +5,20 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestDashboardVersionService(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardVersionService := Service{store: dashboardVersionStore}
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService}
|
||||
|
||||
t.Run("Get dashboard version", func(t *testing.T) {
|
||||
dashboard := &dashver.DashboardVersion{
|
||||
@@ -22,9 +26,11 @@ func TestDashboardVersionService(t *testing.T) {
|
||||
Data: &simplejson.Json{},
|
||||
}
|
||||
dashboardVersionStore.ExpectedDashboardVersion = dashboard
|
||||
dashboardService.On("GetDashboardUIDByID", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).Return(&dashboards.DashboardRef{UID: "uid"}, nil)
|
||||
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(&dashboards.Dashboard{ID: 42}, nil)
|
||||
dashboardVersion, err := dashboardVersionService.Get(context.Background(), &dashver.GetDashboardVersionQuery{})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, dashboard.ToDTO(""), dashboardVersion)
|
||||
require.Equal(t, dashboard.ToDTO("uid"), dashboardVersion)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,7 +39,8 @@ func TestDeleteExpiredVersions(t *testing.T) {
|
||||
setting.DashboardVersionsToKeep = versionsToKeep
|
||||
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardVersionService := Service{store: dashboardVersionStore}
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService}
|
||||
|
||||
t.Run("Don't delete anything if there are no expired versions", func(t *testing.T) {
|
||||
err := dashboardVersionService.DeleteExpired(context.Background(), &dashver.DeleteExpiredVersionsCommand{DeletedRows: 4})
|
||||
@@ -55,15 +62,85 @@ func TestDeleteExpiredVersions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestListDashboardVersions(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardVersionService := Service{store: dashboardVersionStore}
|
||||
t.Run("List all versions for a given Dashboard ID", func(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService}
|
||||
dashboardVersionStore.ExpectedListVersions = []*dashver.DashboardVersion{
|
||||
{ID: 1, DashboardID: 42},
|
||||
}
|
||||
dashboardService.On("GetDashboardUIDByID", mock.Anything,
|
||||
mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).
|
||||
Return(&dashboards.DashboardRef{UID: "uid"}, nil)
|
||||
|
||||
t.Run("Get all versions for a given Dashboard ID", func(t *testing.T) {
|
||||
query := dashver.ListDashboardVersionsQuery{}
|
||||
dashboardVersionStore.ExpectedListVersions = []*dashver.DashboardVersion{{}}
|
||||
query := dashver.ListDashboardVersionsQuery{DashboardID: 42}
|
||||
res, err := dashboardVersionService.List(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(res))
|
||||
// validate that the UID was populated
|
||||
require.EqualValues(t, []*dashver.DashboardVersionDTO{{ID: 1, DashboardID: 42, DashboardUID: "uid"}}, res)
|
||||
})
|
||||
|
||||
t.Run("List all versions for a non-existent DashboardID", func(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService, log: log.NewNopLogger()}
|
||||
dashboardVersionStore.ExpectedListVersions = []*dashver.DashboardVersion{
|
||||
{ID: 1, DashboardID: 42},
|
||||
}
|
||||
dashboardService.On("GetDashboardUIDByID", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardRefByIDQuery")).
|
||||
Return(nil, dashboards.ErrDashboardNotFound).Once()
|
||||
|
||||
query := dashver.ListDashboardVersionsQuery{DashboardID: 42}
|
||||
res, err := dashboardVersionService.List(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(res))
|
||||
// The DashboardID remains populated with the given value, even though the dash was not found
|
||||
require.EqualValues(t, []*dashver.DashboardVersionDTO{{ID: 1, DashboardID: 42}}, res)
|
||||
})
|
||||
|
||||
t.Run("List all versions for a given DashboardUID", func(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService, log: log.NewNopLogger()}
|
||||
dashboardVersionStore.ExpectedListVersions = []*dashver.DashboardVersion{{DashboardID: 42, ID: 1}}
|
||||
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).
|
||||
Return(&dashboards.Dashboard{ID: 42}, nil)
|
||||
|
||||
query := dashver.ListDashboardVersionsQuery{DashboardUID: "uid"}
|
||||
res, err := dashboardVersionService.List(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(res))
|
||||
// validate that the dashboardID was populated from the GetDashboard method call.
|
||||
require.EqualValues(t, []*dashver.DashboardVersionDTO{{ID: 1, DashboardID: 42, DashboardUID: "uid"}}, res)
|
||||
})
|
||||
|
||||
t.Run("List all versions for a given non-existent DashboardUID", func(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService, log: log.NewNopLogger()}
|
||||
dashboardVersionStore.ExpectedListVersions = []*dashver.DashboardVersion{{DashboardID: 42, ID: 1}}
|
||||
dashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).
|
||||
Return(nil, dashboards.ErrDashboardNotFound)
|
||||
|
||||
query := dashver.ListDashboardVersionsQuery{DashboardUID: "uid"}
|
||||
res, err := dashboardVersionService.List(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(res))
|
||||
// validate that the dashboardUID & ID are populated, even though the dash was not found
|
||||
require.EqualValues(t, []*dashver.DashboardVersionDTO{{ID: 1, DashboardID: 42, DashboardUID: "uid"}}, res)
|
||||
})
|
||||
|
||||
t.Run("List Dashboard versions - error from store", func(t *testing.T) {
|
||||
dashboardVersionStore := newDashboardVersionStoreFake()
|
||||
dashboardService := dashboards.NewFakeDashboardService(t)
|
||||
dashboardVersionService := Service{store: dashboardVersionStore, dashSvc: dashboardService, log: log.NewNopLogger()}
|
||||
dashboardVersionStore.ExpectedError = dashver.ErrDashboardVersionNotFound
|
||||
|
||||
query := dashver.ListDashboardVersionsQuery{DashboardID: 42, DashboardUID: "42"}
|
||||
res, err := dashboardVersionService.List(context.Background(), &query)
|
||||
require.Nil(t, res)
|
||||
require.ErrorIs(t, err, dashver.ErrDashboardVersionNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ func testIntegrationGetDashboardVersion(t *testing.T, fn getStore) {
|
||||
|
||||
res, err := dashVerStore.Get(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, query.DashboardID, savedDash.ID)
|
||||
assert.Equal(t, query.Version, savedDash.Version)
|
||||
assert.Equal(t, savedDash.ID, res.ID)
|
||||
assert.Equal(t, savedDash.Version, res.Version)
|
||||
assert.Equal(t, createdById, res.CreatedBy)
|
||||
|
||||
dashCmd := &dashboards.Dashboard{
|
||||
|
||||
@@ -46,10 +46,13 @@ func (v *DashboardVersion) ToDTO(dashUid string) *DashboardVersionDTO {
|
||||
}
|
||||
}
|
||||
|
||||
// GetDashboardVersionQuery is used to Get a dashboard version. Only one of
|
||||
// DashboardID and DashboardUID are required.
|
||||
type GetDashboardVersionQuery struct {
|
||||
DashboardID int64
|
||||
OrgID int64
|
||||
Version int
|
||||
DashboardID int64
|
||||
DashboardUID string
|
||||
OrgID int64
|
||||
Version int
|
||||
}
|
||||
|
||||
type DeleteExpiredVersionsCommand struct {
|
||||
|
||||
@@ -283,9 +283,14 @@ func addAlertRuleMigrations(mg *migrator.Migrator, defaultIntervalSeconds int64)
|
||||
Name: "is_paused",
|
||||
Type: migrator.DB_Bool,
|
||||
Nullable: false,
|
||||
Default: "false",
|
||||
Default: "0",
|
||||
},
|
||||
))
|
||||
|
||||
// This migration fixes a bug where "false" for the default value created a column with default "true" in PostgreSQL databases
|
||||
mg.AddMigration("fix is_paused column for alert_rule table", migrator.NewRawSQLMigration("").
|
||||
Postgres(`ALTER TABLE alert_rule ALTER COLUMN is_paused SET DEFAULT false;
|
||||
UPDATE alert_rule SET is_paused = false;`))
|
||||
}
|
||||
|
||||
func addAlertRuleVersionMigrations(mg *migrator.Migrator) {
|
||||
@@ -347,9 +352,14 @@ func addAlertRuleVersionMigrations(mg *migrator.Migrator) {
|
||||
Name: "is_paused",
|
||||
Type: migrator.DB_Bool,
|
||||
Nullable: false,
|
||||
Default: "false",
|
||||
Default: "0",
|
||||
},
|
||||
))
|
||||
|
||||
// This migration fixes a bug where "false" for the default value created a column with default "true" in PostgreSQL databases
|
||||
mg.AddMigration("fix is_paused column for alert_rule_version table", migrator.NewRawSQLMigration("").
|
||||
Postgres(`ALTER TABLE alert_rule_version ALTER COLUMN is_paused SET DEFAULT false;
|
||||
UPDATE alert_rule_version SET is_paused = false;`))
|
||||
}
|
||||
|
||||
func addAlertmanagerConfigMigrations(mg *migrator.Migrator) {
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
|
||||
const rsIdentifier = `([_a-zA-Z0-9]+)`
|
||||
const sExpr = `\$` + rsIdentifier + `\(([^\)]*)\)`
|
||||
const argFuncExpr = `[\(|,|\s]([_a-zA-Z0-9]+\([^\(]*?\))`
|
||||
|
||||
type msSQLMacroEngine struct {
|
||||
*sqleng.SQLMacroEngineBase
|
||||
@@ -23,41 +22,16 @@ func newMssqlMacroEngine() sqleng.SQLMacroEngine {
|
||||
return &msSQLMacroEngine{SQLMacroEngineBase: sqleng.NewSQLMacroEngineBase()}
|
||||
}
|
||||
|
||||
func replaceFunctionCallArgsWithPlaceholders(sql string) (string, map[string]string) {
|
||||
rExp, _ := regexp.Compile(argFuncExpr)
|
||||
functionCalls := rExp.FindAllString(sql, -1)
|
||||
replacedArgs := make(map[string]string)
|
||||
|
||||
if len(functionCalls) > 0 {
|
||||
sql = strings.Replace(sql, "\n", "", -1)
|
||||
|
||||
for i, functionCall := range functionCalls {
|
||||
key := fmt.Sprintf("$%d", i)
|
||||
formattedVal := functionCall[1:]
|
||||
sql = strings.Replace(sql, formattedVal, key, 1)
|
||||
replacedArgs[key] = formattedVal
|
||||
}
|
||||
|
||||
return sql, replacedArgs
|
||||
}
|
||||
|
||||
return sql, nil
|
||||
}
|
||||
|
||||
func (m *msSQLMacroEngine) Interpolate(query *backend.DataQuery, timeRange backend.TimeRange,
|
||||
sql string) (string, error) {
|
||||
// TODO: Return any error
|
||||
rExp, _ := regexp.Compile(sExpr)
|
||||
var macroError error
|
||||
formattedSql, fctCallArgsMap := replaceFunctionCallArgsWithPlaceholders(sql)
|
||||
|
||||
sql = m.ReplaceAllStringSubmatchFunc(rExp, formattedSql, func(groups []string) string {
|
||||
sql = m.ReplaceAllStringSubmatchFunc(rExp, sql, func(groups []string) string {
|
||||
args := strings.Split(groups[2], ",")
|
||||
for i, arg := range args {
|
||||
args[i] = strings.Trim(arg, " ")
|
||||
if fctCallArgsMap != nil && fctCallArgsMap[args[i]] != "" {
|
||||
args[i] = strings.Replace(args[i], args[i], fctCallArgsMap[args[i]], 1)
|
||||
}
|
||||
}
|
||||
res, err := m.evaluateMacro(timeRange, query, groups[1], args)
|
||||
if err != nil && macroError == nil {
|
||||
|
||||
@@ -208,145 +208,17 @@ func TestMacroEngine(t *testing.T) {
|
||||
require.Equal(t, fmt.Sprintf("select time_column >= %d AND time_column <= %d", from.UnixNano(), to.UnixNano()), sql)
|
||||
})
|
||||
|
||||
t.Run("interpolate __timeGroup with function calls as params successfully", func(t *testing.T) {
|
||||
//various queries with function calls as params tested with different spacings
|
||||
sqls := []struct {
|
||||
query string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
query: "select $__timeGroup(test, 5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', test)/300)*300",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup(try_convert(timestamp, time), 5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup(func(), 5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', func())/300)*300",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup( try_convert(timestamp, time), 5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup( \n try_convert(timestamp, time), 5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup( \n\n\ntry_convert(timestamp, time), 5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup( try_convert(timestamp, time) , 5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup( try_convert(timestamp, time) ,5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup(try_convert(timestamp, time),5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup(try_convert(timestamp, time), 5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup(try_convert(timestamp, time), 5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300",
|
||||
},
|
||||
}
|
||||
|
||||
for _, sql := range sqls {
|
||||
actual, err := engine.Interpolate(query, timeRange, sql.query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, sql.expected, actual)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("interpolate macros with function calls as params successfully", func(t *testing.T) {
|
||||
filterInterpolated := fmt.Sprintf("BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339))
|
||||
unixEpochFilter := fmt.Sprintf("select try_convert(timestamp, time) >= %d AND try_convert(timestamp, time) <= %d", from.Unix(), to.Unix())
|
||||
unixEphocNanoFilter := fmt.Sprintf("select try_convert(timestamp, time) >= %d AND try_convert(timestamp, time) <= %d", from.UnixNano(), to.UnixNano())
|
||||
|
||||
//queries with macros and fct calls as params. fct calls are tested with various params and spaces
|
||||
sqls := []struct {
|
||||
query string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
query: "select $__timeGroup(try_convert(timestamp, time), 5m) from test where $__timeFilter(time)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300 from test where time " + filterInterpolated,
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup(try_convert(timestamp, time), 5m) from test where $__timeFilter(func(time))",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300 from test where func(time) " + filterInterpolated,
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup(try_convert(timestamp, time), 5m) from test where $__timeFilter(func(time));",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300 from test where func(time) " + filterInterpolated + ";",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup(try_convert(timestamp, time), 5m) from test where $__timeFilter(func(time, var2));",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300 from test where func(time, var2) " + filterInterpolated + ";",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup(try_convert(timestamp, time), 5m), $__timeGroup(func2( var1 , var2, var3 ), 15m) from test where $__timeFilter(func(time, var2));",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/300)*300, FLOOR(DATEDIFF(second, '1970-01-01', func2( var1 , var2, var3 ))/900)*900 from test where func(time, var2) " + filterInterpolated + ";",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroupAlias(try_convert(), 5m)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert())/300)*300 AS [time]",
|
||||
},
|
||||
{
|
||||
query: "select $__timeEpoch(try_convert(timestamp, time))",
|
||||
expected: "select DATEDIFF(second, '1970-01-01', try_convert(timestamp, time)) AS time",
|
||||
},
|
||||
{
|
||||
query: "select $__time(try_convert(timestamp, time))",
|
||||
expected: "select try_convert(timestamp, time) AS time",
|
||||
},
|
||||
{
|
||||
query: "select $__timeGroup(try_convert(timestamp, time), 15m, NULL)",
|
||||
expected: "select FLOOR(DATEDIFF(second, '1970-01-01', try_convert(timestamp, time))/900)*900",
|
||||
},
|
||||
{
|
||||
query: "select $__unixEpochFilter(try_convert(timestamp, time))",
|
||||
expected: unixEpochFilter,
|
||||
},
|
||||
{
|
||||
query: "select $__unixEpochNanoFilter(try_convert(timestamp, time))",
|
||||
expected: unixEphocNanoFilter,
|
||||
},
|
||||
{
|
||||
query: "select $__unixEpochGroup(try_convert(timestamp, time), 15m)",
|
||||
expected: "select FLOOR(try_convert(timestamp, time)/900)*900",
|
||||
},
|
||||
{
|
||||
query: "select $__unixEpochGroupAlias(try_convert(timestamp, time), 15m)",
|
||||
expected: "select FLOOR(try_convert(timestamp, time)/900)*900 AS [time]",
|
||||
},
|
||||
}
|
||||
|
||||
for _, sql := range sqls {
|
||||
actual, err := engine.Interpolate(query, timeRange, sql.query)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, sql.expected, actual)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("interpolate macros with function calls as params unsuccessfully", func(t *testing.T) {
|
||||
t.Run("should return unmodified sql if there are no macros present", func(t *testing.T) {
|
||||
sqls := []string{
|
||||
"select $__timeGroup(func1(func2()), 5m)", //cannot go beyond 1st level of nested function calls
|
||||
"select $__timeGroup(func1(), func2())", //second param must be interval
|
||||
"select * from table",
|
||||
"select count(val) from table",
|
||||
"select col1, col2,col3, col4 from table where col1 = 'val1' and col2 = 'val2' order by col1 asc",
|
||||
}
|
||||
|
||||
for _, sql := range sqls {
|
||||
_, err := engine.Interpolate(query, timeRange, sql)
|
||||
require.NotNil(t, err)
|
||||
actual, err := engine.Interpolate(query, timeRange, sql)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, sql, actual)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@grafana-plugins/input-datasource",
|
||||
"version": "9.4.0",
|
||||
"version": "9.4.1",
|
||||
"description": "Input Datasource",
|
||||
"private": true,
|
||||
"repository": {
|
||||
@@ -15,15 +15,15 @@
|
||||
},
|
||||
"author": "Grafana Labs",
|
||||
"devDependencies": {
|
||||
"@grafana/toolkit": "9.4.0",
|
||||
"@grafana/toolkit": "9.4.1",
|
||||
"@types/jest": "26.0.15",
|
||||
"@types/lodash": "4.14.149",
|
||||
"@types/react": "17.0.30",
|
||||
"lodash": "4.17.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/data": "9.4.0",
|
||||
"@grafana/ui": "9.4.0",
|
||||
"@grafana/data": "9.4.1",
|
||||
"@grafana/ui": "9.4.1",
|
||||
"jquery": "3.5.1",
|
||||
"react": "17.0.1",
|
||||
"react-dom": "17.0.1",
|
||||
|
||||
@@ -24,7 +24,7 @@ const data = [
|
||||
{ key: 'jsonkey', value: JSON.stringify({ hello: 'world' }) },
|
||||
];
|
||||
|
||||
const setup = (propOverrides?: KeyValuesTableProps) => {
|
||||
const setup = (propOverrides?: Partial<KeyValuesTableProps>) => {
|
||||
const props = {
|
||||
data: data,
|
||||
...propOverrides,
|
||||
@@ -89,4 +89,28 @@ describe('KeyValuesTable tests', () => {
|
||||
|
||||
expect(screen.getAllByRole('button')).toHaveLength(4);
|
||||
});
|
||||
|
||||
it('renders a link in json and properly escapes it', () => {
|
||||
setup({
|
||||
data: [
|
||||
{ key: 'jsonkey', value: JSON.stringify({ hello: 'https://example.com"id=x tabindex=1 onfocus=alert(1)' }) },
|
||||
],
|
||||
});
|
||||
const link = screen.getByText(/https:\/\/example.com/);
|
||||
expect(link.tagName).toBe('A');
|
||||
expect(link.attributes.getNamedItem('href')?.value).toBe(
|
||||
'https://example.com%22id=x%20tabindex=1%20onfocus=alert(1)'
|
||||
);
|
||||
});
|
||||
|
||||
it('properly escapes json values', () => {
|
||||
setup({
|
||||
data: [
|
||||
{ key: 'jsonkey', value: JSON.stringify({ '<img src=x onerror=alert(1)>': '<img src=x onerror=alert(1)>' }) },
|
||||
],
|
||||
});
|
||||
const values = screen.getAllByText(/onerror=alert/);
|
||||
expect(values[0].innerHTML).toBe('"<img src=x onerror=alert(1)>":');
|
||||
expect(values[1].innerHTML).toBe('"<img src=x onerror=alert(1)>"');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
import { css } from '@emotion/css';
|
||||
import cx from 'classnames';
|
||||
import jsonMarkup from 'json-markup';
|
||||
import * as React from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
@@ -25,6 +24,8 @@ import CopyIcon from '../../common/CopyIcon';
|
||||
import { TraceKeyValuePair, TraceLink, TNil } from '../../types';
|
||||
import { ubInlineBlock, uWidth100 } from '../../uberUtilityStyles';
|
||||
|
||||
import jsonMarkup from './jsonMarkup';
|
||||
|
||||
const copyIconClassName = 'copyIcon';
|
||||
|
||||
export const getStyles = (theme: GrafanaTheme2) => {
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2014 Mathias Buus
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
const INDENT = ' ';
|
||||
|
||||
function inlineRule(objRule) {
|
||||
let str = '';
|
||||
objRule &&
|
||||
Object.keys(objRule).forEach(function (rule) {
|
||||
str += rule + ':' + objRule[rule] + ';';
|
||||
});
|
||||
return str;
|
||||
}
|
||||
|
||||
function Stylize(styleFile) {
|
||||
function styleClass(cssClass) {
|
||||
return 'class="' + cssClass + '"';
|
||||
}
|
||||
|
||||
function styleInline(cssClass) {
|
||||
return 'style="' + inlineRule(styleFile['.' + cssClass]) + '"';
|
||||
}
|
||||
|
||||
if (!styleFile) {
|
||||
return styleClass;
|
||||
}
|
||||
return styleInline;
|
||||
}
|
||||
|
||||
function type(doc) {
|
||||
if (doc === null) {
|
||||
return 'null';
|
||||
}
|
||||
if (Array.isArray(doc)) {
|
||||
return 'array';
|
||||
}
|
||||
if (typeof doc === 'string' && /^https?:/.test(doc)) {
|
||||
return 'link';
|
||||
}
|
||||
if (typeof doc === 'object' && typeof doc.toISOString === 'function') {
|
||||
return 'date';
|
||||
}
|
||||
|
||||
return typeof doc;
|
||||
}
|
||||
|
||||
function escape(str) {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
module.exports = function (doc, styleFile) {
|
||||
let indent = '';
|
||||
const style = Stylize(styleFile);
|
||||
|
||||
let forEach = function (list, start, end, fn) {
|
||||
if (!list.length) {
|
||||
return start + ' ' + end;
|
||||
}
|
||||
|
||||
let out = start + '\n';
|
||||
|
||||
indent += INDENT;
|
||||
list.forEach(function (key, i) {
|
||||
out += indent + fn(key) + (i < list.length - 1 ? ',' : '') + '\n';
|
||||
});
|
||||
indent = indent.slice(0, -INDENT.length);
|
||||
|
||||
return out + indent + end;
|
||||
};
|
||||
|
||||
function visit(obj) {
|
||||
if (obj === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
switch (type(obj)) {
|
||||
case 'boolean':
|
||||
return '<span ' + style('json-markup-bool') + '>' + obj + '</span>';
|
||||
|
||||
case 'number':
|
||||
return '<span ' + style('json-markup-number') + '>' + obj + '</span>';
|
||||
|
||||
case 'date':
|
||||
return '<span class="json-markup-string">"' + escape(obj.toISOString()) + '"</span>';
|
||||
|
||||
case 'null':
|
||||
return '<span ' + style('json-markup-null') + '>null</span>';
|
||||
|
||||
case 'string':
|
||||
return '<span ' + style('json-markup-string') + '>"' + escape(obj.replace(/\n/g, '\n' + indent)) + '"</span>';
|
||||
|
||||
case 'link':
|
||||
return (
|
||||
'<span ' + style('json-markup-string') + '>"<a href="' + encodeURI(obj) + '">' + escape(obj) + '</a>"</span>'
|
||||
);
|
||||
|
||||
case 'array':
|
||||
return forEach(obj, '[', ']', visit);
|
||||
|
||||
case 'object':
|
||||
const keys = Object.keys(obj).filter(function (key) {
|
||||
return obj[key] !== undefined;
|
||||
});
|
||||
|
||||
return forEach(keys, '{', '}', function (key) {
|
||||
return '<span ' + style('json-markup-key') + '>"' + escape(key) + '":</span> ' + visit(obj[key]);
|
||||
});
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
return '<div ' + style('json-markup') + '>' + visit(doc) + '</div>';
|
||||
};
|
||||
@@ -180,7 +180,7 @@ export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery,
|
||||
|
||||
applyTemplateVariables(query: InfluxQuery, scopedVars: ScopedVars): Record<string, any> {
|
||||
// We want to interpolate these variables on backend
|
||||
const { __interval, __interval_ms, ...rest } = scopedVars;
|
||||
const { __interval, __interval_ms, ...rest } = scopedVars || {};
|
||||
|
||||
if (this.isFlux) {
|
||||
return {
|
||||
@@ -403,10 +403,6 @@ export default class InfluxDatasource extends DataSourceWithBackend<InfluxQuery,
|
||||
params.db = this.database;
|
||||
}
|
||||
|
||||
if (options?.policy) {
|
||||
params.rp = options.policy;
|
||||
}
|
||||
|
||||
const { q } = data;
|
||||
|
||||
if (method === 'POST' && has(data, 'q')) {
|
||||
|
||||
@@ -11,8 +11,7 @@ const runExploreQuery = (
|
||||
): Promise<Array<{ text: string }>> => {
|
||||
const builder = new InfluxQueryBuilder(target, datasource.database);
|
||||
const q = builder.buildExploreQuery(type, withKey, withMeasurementFilter);
|
||||
const options = { policy: target.policy };
|
||||
return datasource.metricFindQuery(q, options);
|
||||
return datasource.metricFindQuery(q);
|
||||
};
|
||||
|
||||
export async function getAllPolicies(datasource: InfluxDatasource): Promise<string[]> {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Map as OpenLayersMap } from 'ol';
|
||||
import { FeatureLike } from 'ol/Feature';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { getFrameMatchers, MapLayerHandler, MapLayerOptions, PanelData } from '@grafana/data/src';
|
||||
import { getFrameMatchers, MapLayerHandler, MapLayerOptions, PanelData, textUtil } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime/src';
|
||||
|
||||
import { GeomapPanel } from '../GeomapPanel';
|
||||
@@ -114,6 +114,10 @@ export async function initLayer(
|
||||
return Promise.reject('unknown layer: ' + options.type);
|
||||
}
|
||||
|
||||
if (options.config?.attribution) {
|
||||
options.config.attribution = textUtil.sanitizeTextPanelContent(options.config.attribution);
|
||||
}
|
||||
|
||||
const handler = await item.create(map, options, panel.props.eventBus, config.theme2);
|
||||
const layer = handler.init(); // eslint-disable-line
|
||||
if (options.opacity != null) {
|
||||
|
||||
@@ -1106,7 +1106,8 @@ def redis_integration_tests_step():
|
||||
},
|
||||
"commands": [
|
||||
"dockerize -wait tcp://redis:6379/0 -timeout 120s",
|
||||
"./bin/grabpl integration-tests",
|
||||
"go clean -testcache",
|
||||
"go list './pkg/...' | xargs -I {} sh -c 'go test -run Integration -covermode=atomic -timeout=5m {}'",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
42
yarn.lock
42
yarn.lock
@@ -4815,9 +4815,9 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana-plugins/input-datasource@workspace:plugins-bundled/internal/input-datasource"
|
||||
dependencies:
|
||||
"@grafana/data": 9.4.0
|
||||
"@grafana/toolkit": 9.4.0
|
||||
"@grafana/ui": 9.4.0
|
||||
"@grafana/data": 9.4.1
|
||||
"@grafana/toolkit": 9.4.1
|
||||
"@grafana/ui": 9.4.1
|
||||
"@types/jest": 26.0.15
|
||||
"@types/lodash": 4.14.149
|
||||
"@types/react": 17.0.30
|
||||
@@ -4838,12 +4838,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/data@9.4.0, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data":
|
||||
"@grafana/data@9.4.1, @grafana/data@workspace:*, @grafana/data@workspace:packages/grafana-data":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/data@workspace:packages/grafana-data"
|
||||
dependencies:
|
||||
"@braintree/sanitize-url": 6.0.1
|
||||
"@grafana/schema": 9.4.0
|
||||
"@grafana/schema": 9.4.1
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@rollup/plugin-commonjs": 23.0.2
|
||||
"@rollup/plugin-json": 5.0.1
|
||||
@@ -4901,7 +4901,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana/e2e-selectors@9.4.0, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors":
|
||||
"@grafana/e2e-selectors@9.4.1, @grafana/e2e-selectors@workspace:*, @grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/e2e-selectors@workspace:packages/grafana-e2e-selectors"
|
||||
dependencies:
|
||||
@@ -4938,7 +4938,7 @@ __metadata:
|
||||
"@babel/core": 7.20.5
|
||||
"@babel/preset-env": 7.20.2
|
||||
"@cypress/webpack-preprocessor": 5.16.0
|
||||
"@grafana/e2e-selectors": 9.4.0
|
||||
"@grafana/e2e-selectors": 9.4.1
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@mochajs/json-file-reporter": ^1.2.0
|
||||
"@rollup/plugin-node-resolve": 15.0.1
|
||||
@@ -5110,11 +5110,11 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/runtime@workspace:packages/grafana-runtime"
|
||||
dependencies:
|
||||
"@grafana/data": 9.4.0
|
||||
"@grafana/e2e-selectors": 9.4.0
|
||||
"@grafana/data": 9.4.1
|
||||
"@grafana/e2e-selectors": 9.4.1
|
||||
"@grafana/faro-web-sdk": 1.0.0-beta2
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@grafana/ui": 9.4.0
|
||||
"@grafana/ui": 9.4.1
|
||||
"@rollup/plugin-commonjs": 23.0.2
|
||||
"@rollup/plugin-node-resolve": 15.0.1
|
||||
"@sentry/browser": 6.19.7
|
||||
@@ -5165,7 +5165,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/schema@9.4.0, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema":
|
||||
"@grafana/schema@9.4.1, @grafana/schema@workspace:*, @grafana/schema@workspace:packages/grafana-schema":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/schema@workspace:packages/grafana-schema"
|
||||
dependencies:
|
||||
@@ -5184,7 +5184,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana/toolkit@9.4.0, @grafana/toolkit@workspace:*, @grafana/toolkit@workspace:packages/grafana-toolkit":
|
||||
"@grafana/toolkit@9.4.1, @grafana/toolkit@workspace:*, @grafana/toolkit@workspace:packages/grafana-toolkit":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/toolkit@workspace:packages/grafana-toolkit"
|
||||
dependencies:
|
||||
@@ -5200,10 +5200,10 @@ __metadata:
|
||||
"@babel/preset-env": 7.18.9
|
||||
"@babel/preset-react": 7.18.6
|
||||
"@babel/preset-typescript": 7.18.6
|
||||
"@grafana/data": 9.4.0
|
||||
"@grafana/data": 9.4.1
|
||||
"@grafana/eslint-config": 5.0.0
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@grafana/ui": 9.4.0
|
||||
"@grafana/ui": 9.4.1
|
||||
"@jest/core": 27.5.1
|
||||
"@types/command-exists": ^1.2.0
|
||||
"@types/eslint": 8.4.1
|
||||
@@ -5284,16 +5284,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@grafana/ui@9.4.0, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui":
|
||||
"@grafana/ui@9.4.1, @grafana/ui@workspace:*, @grafana/ui@workspace:packages/grafana-ui":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@grafana/ui@workspace:packages/grafana-ui"
|
||||
dependencies:
|
||||
"@babel/core": 7.20.5
|
||||
"@emotion/css": 11.10.5
|
||||
"@emotion/react": 11.10.5
|
||||
"@grafana/data": 9.4.0
|
||||
"@grafana/e2e-selectors": 9.4.0
|
||||
"@grafana/schema": 9.4.0
|
||||
"@grafana/data": 9.4.1
|
||||
"@grafana/e2e-selectors": 9.4.1
|
||||
"@grafana/schema": 9.4.1
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
"@leeoniya/ufuzzy": 0.9.0
|
||||
"@mdx-js/react": 1.6.22
|
||||
@@ -18444,9 +18444,9 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"dompurify@npm:^2.4.1":
|
||||
version: 2.4.3
|
||||
resolution: "dompurify@npm:2.4.3"
|
||||
checksum: b440981f2a38cada2085759cc3d1e2f94571afc34343d011a8a6aa1ad91ae6abf651adbfa4994b0e2283f0ce81f7891cdb04b67d0b234c8d190cb70e9691f026
|
||||
version: 2.4.1
|
||||
resolution: "dompurify@npm:2.4.1"
|
||||
checksum: 1169177465b3cbb25a44322937fba549f6c4e1a91b83245d144471be26619c835cccf0f8e20aa78c25ac11a06efd17cc1b9db9cacadceb78a4c08a1029eafee5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user