Revert: Future-proofing query and data source model in Dashboard Sche… (#107985)

* Revert: Future-proofing query and data source model in Dashboard Schema v2

---------

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
pull/106996/head
Stephanie Hingtgen 1 week ago committed by GitHub
parent 18b5a9eb8d
commit 13a89d4ae3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      apps/alerting/notifications/Makefile
  2. 2
      apps/dashboard/Makefile
  3. 18
      apps/dashboard/kinds/v2alpha1/dashboard_spec.cue
  4. 18
      apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue
  5. 96
      apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go
  6. 79
      apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go
  7. 2
      apps/iam/pkg/apis/iam_manifest.go
  8. 604
      conf/provisioning/sample/dashboard-v2.json
  9. 67
      e2e/dashboards/TestV2Dashboard.json
  10. 4
      e2e/utils/flows/scenes/importDashboard.ts
  11. 9
      go.work.sum
  12. 68
      packages/grafana-schema/src/schema/dashboard/v2_examples.ts
  13. 42
      packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen.ts
  14. 45
      pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json
  15. 52
      public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.test.ts
  16. 20
      public/app/features/dashboard-scene/scene/export/exporters.test.ts
  17. 5
      public/app/features/dashboard-scene/scene/export/exporters.ts
  18. 29
      public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.test.ts
  19. 6
      public/app/features/dashboard-scene/serialization/DashboardSceneSerializer.ts
  20. 47
      public/app/features/dashboard-scene/serialization/__snapshots__/transformSceneToSaveModelSchemaV2.test.ts.snap
  21. 62
      public/app/features/dashboard-scene/serialization/layoutSerializers/utils.test.ts
  22. 45
      public/app/features/dashboard-scene/serialization/layoutSerializers/utils.ts
  23. 137
      public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts
  24. 22
      public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts
  25. 88
      public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.test.ts
  26. 47
      public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts
  27. 61
      public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.test.ts
  28. 67
      public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts
  29. 39
      public/app/features/dashboard-scene/v2schema/ImportDashboardOverviewV2.tsx
  30. 3
      public/app/features/dashboard-scene/v2schema/test-helpers.ts
  31. 57
      public/app/features/dashboard/api/ResponseTransformers.test.ts
  32. 38
      public/app/features/dashboard/api/ResponseTransformers.ts
  33. 25
      public/app/features/manage-dashboards/state/actions.test.ts
  34. 59
      public/app/features/manage-dashboards/state/actions.ts

@ -19,6 +19,9 @@ update-app-sdk: ## Update the Grafana App SDK dependency in go.mod
go mod tidy
.PHONY: generate
generate: install-app-sdk update-app-sdk
generate: do-generate ## Run Grafana App SDK code generation
.PHONY: do-generate
do-generate: install-app-sdk update-app-sdk
## --defencoding=none and noschemasinmanifest are needed to avoid infinite loop while generating recursive models (see routingtree.cue)
@$(APP_SDK_BIN) generate --grouping=group --gogenpath=./pkg/apis --defencoding=none --postprocess --noschemasinmanifest

@ -1,4 +1,4 @@
APP_SDK_VERSION := v0.39.2
APP_SDK_VERSION := v0.39.0
APP_SDK_DIR := $(shell go env GOPATH)/bin/app-sdk-$(APP_SDK_VERSION)
APP_SDK_BIN := $(APP_SDK_DIR)/grafana-app-sdk

@ -111,8 +111,6 @@ DashboardLink: {
keepTime: bool | *false
}
// Keeping this for backwards compatibility for GroupByVariableSpec and AdhocVariableSpec
// This type is widely used in the codebase and changing it will have a big impact
DataSourceRef: {
// The plugin type-id
type?: string
@ -387,7 +385,8 @@ VizConfigKind: {
}
AnnotationQuerySpec: {
query: DataQueryKind
datasource?: DataSourceRef
query?: DataQueryKind
enable: bool
hide: bool
iconColor: string
@ -413,19 +412,15 @@ QueryOptionsSpec: {
}
DataQueryKind: {
kind: "DataQuery"
group: string
version: string | *"v0"
// New type for datasource reference
// Not creating a new type until we figure out how to handle DS refs for group by, adhoc, and every place that uses DataSourceRef in TS.
datasource?: {
name?: string
}
// The kind of a DataQueryKind is the datasource type
kind: string
spec: [string]: _
}
PanelQuerySpec: {
query: DataQueryKind
datasource?: DataSourceRef
refId: string
hidden: bool
}
@ -728,6 +723,7 @@ QueryVariableSpec: {
refresh: VariableRefresh
skipUrlSync: bool | *false
description?: string
datasource?: DataSourceRef
query: DataQueryKind
regex: string | *""
sort: VariableSort

@ -115,8 +115,6 @@ DashboardLink: {
keepTime: bool | *false
}
// Keeping this for backwards compatibility for GroupByVariableSpec and AdhocVariableSpec
// This type is widely used in the codebase and changing it will have a big impact
DataSourceRef: {
// The plugin type-id
type?: string
@ -391,7 +389,8 @@ VizConfigKind: {
}
AnnotationQuerySpec: {
query: DataQueryKind
datasource?: DataSourceRef
query?: DataQueryKind
enable: bool
hide: bool
iconColor: string
@ -417,19 +416,15 @@ QueryOptionsSpec: {
}
DataQueryKind: {
kind: "DataQuery"
group: string
version: string | *"v0"
// New type for datasource reference
// Not creating a new type until we figure out how to handle DS refs for group by, adhoc, and every place that uses DataSourceRef in TS.
datasource?: {
name?: string
}
// The kind of a DataQueryKind is the datasource type
kind: string
spec: [string]: _
}
PanelQuerySpec: {
query: DataQueryKind
datasource?: DataSourceRef
refId: string
hidden: bool
}
@ -732,6 +727,7 @@ QueryVariableSpec: {
refresh: VariableRefresh
skipUrlSync: bool | *false
description?: string
datasource?: DataSourceRef
query: DataQueryKind
regex: string | *""
sort: VariableSort

@ -23,13 +23,14 @@ func NewDashboardAnnotationQueryKind() *DashboardAnnotationQueryKind {
// +k8s:openapi-gen=true
type DashboardAnnotationQuerySpec struct {
Query DashboardDataQueryKind `json:"query"`
Enable bool `json:"enable"`
Hide bool `json:"hide"`
IconColor string `json:"iconColor"`
Name string `json:"name"`
BuiltIn *bool `json:"builtIn,omitempty"`
Filter *DashboardAnnotationPanelFilter `json:"filter,omitempty"`
Datasource *DashboardDataSourceRef `json:"datasource,omitempty"`
Query *DashboardDataQueryKind `json:"query,omitempty"`
Enable bool `json:"enable"`
Hide bool `json:"hide"`
IconColor string `json:"iconColor"`
Name string `json:"name"`
BuiltIn *bool `json:"builtIn,omitempty"`
Filter *DashboardAnnotationPanelFilter `json:"filter,omitempty"`
// Catch-all field for datasource-specific properties
LegacyOptions map[string]interface{} `json:"legacyOptions,omitempty"`
}
@ -37,28 +38,34 @@ type DashboardAnnotationQuerySpec struct {
// NewDashboardAnnotationQuerySpec creates a new DashboardAnnotationQuerySpec object.
func NewDashboardAnnotationQuerySpec() *DashboardAnnotationQuerySpec {
return &DashboardAnnotationQuerySpec{
Query: *NewDashboardDataQueryKind(),
BuiltIn: (func(input bool) *bool { return &input })(false),
}
}
// +k8s:openapi-gen=true
type DashboardDataSourceRef struct {
// The plugin type-id
Type *string `json:"type,omitempty"`
// Specific datasource instance
Uid *string `json:"uid,omitempty"`
}
// NewDashboardDataSourceRef creates a new DashboardDataSourceRef object.
func NewDashboardDataSourceRef() *DashboardDataSourceRef {
return &DashboardDataSourceRef{}
}
// +k8s:openapi-gen=true
type DashboardDataQueryKind struct {
Kind string `json:"kind"`
Group string `json:"group"`
Version string `json:"version"`
// New type for datasource reference
// Not creating a new type until we figure out how to handle DS refs for group by, adhoc, and every place that uses DataSourceRef in TS.
Datasource *DashboardV2alpha1DataQueryKindDatasource `json:"datasource,omitempty"`
Spec map[string]interface{} `json:"spec"`
// The kind of a DataQueryKind is the datasource type
Kind string `json:"kind"`
Spec map[string]interface{} `json:"spec"`
}
// NewDashboardDataQueryKind creates a new DashboardDataQueryKind object.
func NewDashboardDataQueryKind() *DashboardDataQueryKind {
return &DashboardDataQueryKind{
Kind: "DataQuery",
Version: "v0",
Spec: map[string]interface{}{},
Spec: map[string]interface{}{},
}
}
@ -192,9 +199,10 @@ func NewDashboardPanelQueryKind() *DashboardPanelQueryKind {
// +k8s:openapi-gen=true
type DashboardPanelQuerySpec struct {
Query DashboardDataQueryKind `json:"query"`
RefId string `json:"refId"`
Hidden bool `json:"hidden"`
Query DashboardDataQueryKind `json:"query"`
Datasource *DashboardDataSourceRef `json:"datasource,omitempty"`
RefId string `json:"refId"`
Hidden bool `json:"hidden"`
}
// NewDashboardPanelQuerySpec creates a new DashboardPanelQuerySpec object.
@ -755,9 +763,7 @@ type DashboardRepeatOptions struct {
// NewDashboardRepeatOptions creates a new DashboardRepeatOptions object.
func NewDashboardRepeatOptions() *DashboardRepeatOptions {
return &DashboardRepeatOptions{
Mode: DashboardRepeatMode,
}
return &DashboardRepeatOptions{}
}
// other repeat modes will be added in the future: label, frame
@ -932,9 +938,7 @@ type DashboardRowRepeatOptions struct {
// NewDashboardRowRepeatOptions creates a new DashboardRowRepeatOptions object.
func NewDashboardRowRepeatOptions() *DashboardRowRepeatOptions {
return &DashboardRowRepeatOptions{
Mode: DashboardRepeatMode,
}
return &DashboardRowRepeatOptions{}
}
// +k8s:openapi-gen=true
@ -1007,9 +1011,7 @@ type DashboardAutoGridRepeatOptions struct {
// NewDashboardAutoGridRepeatOptions creates a new DashboardAutoGridRepeatOptions object.
func NewDashboardAutoGridRepeatOptions() *DashboardAutoGridRepeatOptions {
return &DashboardAutoGridRepeatOptions{
Mode: DashboardRepeatMode,
}
return &DashboardAutoGridRepeatOptions{}
}
// +k8s:openapi-gen=true
@ -1075,9 +1077,7 @@ type DashboardTabRepeatOptions struct {
// NewDashboardTabRepeatOptions creates a new DashboardTabRepeatOptions object.
func NewDashboardTabRepeatOptions() *DashboardTabRepeatOptions {
return &DashboardTabRepeatOptions{
Mode: DashboardRepeatMode,
}
return &DashboardTabRepeatOptions{}
}
// Links with references to other dashboards or external resources
@ -1221,6 +1221,7 @@ type DashboardQueryVariableSpec struct {
Refresh DashboardVariableRefresh `json:"refresh"`
SkipUrlSync bool `json:"skipUrlSync"`
Description *string `json:"description,omitempty"`
Datasource *DashboardDataSourceRef `json:"datasource,omitempty"`
Query DashboardDataQueryKind `json:"query"`
Regex string `json:"regex"`
Sort DashboardVariableSort `json:"sort"`
@ -1626,21 +1627,6 @@ func NewDashboardGroupByVariableSpec() *DashboardGroupByVariableSpec {
}
}
// Keeping this for backwards compatibility for GroupByVariableSpec and AdhocVariableSpec
// This type is widely used in the codebase and changing it will have a big impact
// +k8s:openapi-gen=true
type DashboardDataSourceRef struct {
// The plugin type-id
Type *string `json:"type,omitempty"`
// Specific datasource instance
Uid *string `json:"uid,omitempty"`
}
// NewDashboardDataSourceRef creates a new DashboardDataSourceRef object.
func NewDashboardDataSourceRef() *DashboardDataSourceRef {
return &DashboardDataSourceRef{}
}
// Adhoc variable kind
// +k8s:openapi-gen=true
type DashboardAdhocVariableKind struct {
@ -1701,9 +1687,7 @@ type DashboardAdHocFilterWithLabels struct {
// NewDashboardAdHocFilterWithLabels creates a new DashboardAdHocFilterWithLabels object.
func NewDashboardAdHocFilterWithLabels() *DashboardAdHocFilterWithLabels {
return &DashboardAdHocFilterWithLabels{
Origin: DashboardFilterOrigin,
}
return &DashboardAdHocFilterWithLabels{}
}
// Determine the origin of the adhoc variable filter
@ -1774,16 +1758,6 @@ func NewDashboardSpec() *DashboardSpec {
}
}
// +k8s:openapi-gen=true
type DashboardV2alpha1DataQueryKindDatasource struct {
Name *string `json:"name,omitempty"`
}
// NewDashboardV2alpha1DataQueryKindDatasource creates a new DashboardV2alpha1DataQueryKindDatasource object.
func NewDashboardV2alpha1DataQueryKindDatasource() *DashboardV2alpha1DataQueryKindDatasource {
return &DashboardV2alpha1DataQueryKindDatasource{}
}
// +k8s:openapi-gen=true
type DashboardV2alpha1FieldConfigSourceOverrides struct {
Matcher DashboardMatcherConfig `json:"matcher"`

@ -109,7 +109,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardTimeRangeOption": schema_pkg_apis_dashboard_v2alpha1_DashboardTimeRangeOption(ref),
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardTimeSettingsSpec": schema_pkg_apis_dashboard_v2alpha1_DashboardTimeSettingsSpec(ref),
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardTransformationKind": schema_pkg_apis_dashboard_v2alpha1_DashboardTransformationKind(ref),
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1DataQueryKindDatasource": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1DataQueryKindDatasource(ref),
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1FieldConfigSourceOverrides": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1FieldConfigSourceOverrides(ref),
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RangeMapOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1RangeMapOptions(ref),
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1RegexMapOptions": schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1RegexMapOptions(ref),
@ -596,10 +595,14 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardAnnotationQuerySpec(ref common.
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"datasource": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"),
},
},
"query": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind"),
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind"),
},
},
"enable": {
@ -657,11 +660,11 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardAnnotationQuerySpec(ref common.
},
},
},
Required: []string{"query", "enable", "hide", "iconColor", "name"},
Required: []string{"enable", "hide", "iconColor", "name"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAnnotationPanelFilter", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind"},
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAnnotationPanelFilter", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"},
}
}
@ -1502,29 +1505,10 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardDataQueryKind(ref common.Refere
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"group": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"version": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"datasource": {
SchemaProps: spec.SchemaProps{
Description: "New type for datasource reference Not creating a new type until we figure out how to handle DS refs for group by, adhoc, and every place that uses DataSourceRef in TS.",
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1DataQueryKindDatasource"),
Description: "The kind of a DataQueryKind is the datasource type",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"spec": {
@ -1542,11 +1526,9 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardDataQueryKind(ref common.Refere
},
},
},
Required: []string{"kind", "group", "version", "spec"},
Required: []string{"kind", "spec"},
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardV2alpha1DataQueryKindDatasource"},
}
}
@ -1554,8 +1536,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardDataSourceRef(ref common.Refere
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "Keeping this for backwards compatibility for GroupByVariableSpec and AdhocVariableSpec This type is widely used in the codebase and changing it will have a big impact",
Type: []string{"object"},
Type: []string{"object"},
Properties: map[string]spec.Schema{
"type": {
SchemaProps: spec.SchemaProps{
@ -2909,6 +2890,11 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardPanelQuerySpec(ref common.Refer
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind"),
},
},
"datasource": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"),
},
},
"refId": {
SchemaProps: spec.SchemaProps{
Default: "",
@ -2928,7 +2914,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardPanelQuerySpec(ref common.Refer
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind"},
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"},
}
}
@ -3264,6 +3250,11 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableSpec(ref common.Re
Format: "",
},
},
"datasource": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef"),
},
},
"query": {
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
@ -3341,7 +3332,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableSpec(ref common.Re
},
},
Dependencies: []string{
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"},
"github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataQueryKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardDataSourceRef", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"},
}
}
@ -4329,24 +4320,6 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardTransformationKind(ref common.R
}
}
func schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1DataQueryKindDatasource(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"name": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
}
}
func schema_pkg_apis_dashboard_v2alpha1_DashboardV2alpha1FieldConfigSourceOverrides(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{

@ -14,6 +14,8 @@ import (
v0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
)
var ()
var appManifestData = app.ManifestData{
AppName: "iam",
Group: "iam.grafana.app",

@ -10,44 +10,14 @@
"kind": "AnnotationQuery",
"spec": {
"builtIn": true,
"enable": true,
"filter": {
"exclude": false,
"ids": [1]
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"query": {
"group": "grafana",
"kind": "DataQuery",
"spec": {},
"version": "v0"
}
}
},
{
"kind": "AnnotationQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "grafana-testdata-datasource",
"version": "v0",
"spec": {
"lines": 10,
"refId": "Anno",
"scenarioId": "annotations"
}
},
"enable": true,
"hide": false,
"iconColor": "red",
"name": "Test data annotations",
"builtIn": false,
"filter": {
"exclude": false,
"ids": [1]
}
"name": "Annotations \u0026 Alerts"
}
}
],
@ -67,16 +37,8 @@
"spec": {
"hidden": false,
"query": {
"datasource": {
"name": "gdev-testdata"
},
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {
"scenarioId": "random_walk",
"seriesCount": 3
},
"version": "v0"
"kind": "grafana-testdata-datasource",
"spec": {}
},
"refId": "A"
}
@ -89,7 +51,7 @@
"description": "",
"id": 1,
"links": [],
"title": "Simple timeseries (WITH DS REF)",
"title": "Simle timeseries",
"vizConfig": {
"kind": "timeseries",
"spec": {
@ -131,12 +93,12 @@
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
"color": "green"
},
{
"color": "red",
@ -160,7 +122,7 @@
"sort": "none"
}
},
"pluginVersion": "12.1.0-pre"
"pluginVersion": "12.0.0-pre"
}
}
}
@ -177,13 +139,8 @@
"spec": {
"hidden": false,
"query": {
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {
"scenarioId": "random_walk",
"seriesCount": 4
},
"version": "v0"
"kind": "grafana-testdata-datasource",
"spec": {}
},
"refId": "A"
}
@ -196,7 +153,7 @@
"description": "",
"id": 2,
"links": [],
"title": "Simple stat (NO DS REF)",
"title": "Simple stat",
"vizConfig": {
"kind": "stat",
"spec": {
@ -205,12 +162,12 @@
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
"color": "green"
},
{
"color": "red",
@ -236,496 +193,7 @@
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "12.1.0-pre"
}
}
}
},
"panel-3": {
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"group": "prometheus",
"kind": "DataQuery",
"spec": {
"disableTextWrap": false,
"editorMode": "builder",
"expr": "rate(counters_requests[$__rate_interval])",
"fullMetaSearch": false,
"includeNullMetadata": false,
"instant": false,
"legendFormat": "__auto",
"range": true,
"useBackend": false
},
"version": "v0"
},
"refId": "A"
}
}
],
"queryOptions": {},
"transformations": []
}
},
"description": "",
"id": 3,
"links": [],
"title": "Panel with NO REF to gdev-prometheus",
"vizConfig": {
"kind": "timeseries",
"spec": {
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.1.0-pre"
}
}
}
},
"panel-4": {
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"datasource": {
"name": "gdev-prometheus"
},
"group": "prometheus",
"kind": "DataQuery",
"spec": {
"disableTextWrap": false,
"editorMode": "builder",
"expr": "rate(counters_requests[$__rate_interval])",
"fullMetaSearch": false,
"includeNullMetadata": false,
"instant": false,
"legendFormat": "__auto",
"range": true,
"useBackend": false
},
"version": "v0"
},
"refId": "A"
}
}
],
"queryOptions": {},
"transformations": []
}
},
"description": "",
"id": 4,
"links": [],
"title": "Panel with ref to gdev-prometheus",
"vizConfig": {
"kind": "timeseries",
"spec": {
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.1.0-pre"
}
}
}
},
"panel-5": {
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"datasource": {
"name": "gdev-prometheus"
},
"group": "prometheus",
"kind": "DataQuery",
"spec": {
"disableTextWrap": false,
"editorMode": "builder",
"expr": "rate(counters_requests{server=\"backend-01\"}[$__rate_interval])",
"fullMetaSearch": false,
"includeNullMetadata": false,
"legendFormat": "__auto",
"range": true,
"useBackend": false
},
"version": "v0"
},
"refId": "A"
}
},
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"datasource": {
"name": "gdev-testdata"
},
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {},
"version": "v0"
},
"refId": "B"
}
}
],
"queryOptions": {},
"transformations": []
}
},
"description": "",
"id": 5,
"links": [],
"title": "Mixed DS WITH REFS",
"vizConfig": {
"kind": "timeseries",
"spec": {
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.1.0-pre"
}
}
}
},
"panel-6": {
"kind": "Panel",
"spec": {
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"group": "prometheus",
"kind": "DataQuery",
"spec": {
"disableTextWrap": false,
"editorMode": "builder",
"expr": "rate(counters_requests{server=\"backend-01\"}[$__rate_interval])",
"fullMetaSearch": false,
"includeNullMetadata": false,
"legendFormat": "__auto",
"range": true,
"useBackend": false
},
"version": "v0"
},
"refId": "A"
}
},
{
"kind": "PanelQuery",
"spec": {
"hidden": false,
"query": {
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {},
"version": "v0"
},
"refId": "B"
}
}
],
"queryOptions": {},
"transformations": []
}
},
"description": "",
"id": 6,
"links": [],
"title": "Mixed DS WITHOUT REFS",
"vizConfig": {
"kind": "timeseries",
"spec": {
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.1.0-pre"
"pluginVersion": "12.0.0-pre"
}
}
}
@ -736,24 +204,6 @@
"spec": {
"columnWidthMode": "standard",
"items": [
{
"kind": "AutoGridLayoutItem",
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-3"
}
}
},
{
"kind": "AutoGridLayoutItem",
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-4"
}
}
},
{
"kind": "AutoGridLayoutItem",
"spec": {
@ -771,24 +221,6 @@
"name": "panel-1"
}
}
},
{
"kind": "AutoGridLayoutItem",
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-5"
}
}
},
{
"kind": "AutoGridLayoutItem",
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-6"
}
}
}
],
"maxColumnCount": 3,
@ -803,7 +235,7 @@
"autoRefresh": "",
"autoRefreshIntervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
"fiscalYearStartMonth": 0,
"from": "now-5m",
"from": "now-6h",
"hideTimepicker": false,
"timezone": "browser",
"to": "now"

@ -2,11 +2,21 @@
"apiVersion": "dashboard.grafana.app/v2alpha1",
"kind": "Dashboard",
"metadata": {
"name": "fa400625-2a44-4add-a369-e6c972eb4bd6",
"name": "admjzp8",
"namespace": "default",
"uid": "hrbekBWXeM7nC7GtouIbcngFyuqt8Xx7KlE4AnTwV7AX",
"resourceVersion": "1",
"generation": 1,
"creationTimestamp": "2025-05-27T11:40:22Z",
"labels": {},
"annotations": {}
"creationTimestamp": "2025-05-16T09:41:56Z",
"labels": {
"grafana.app/deprecatedInternalID": "182"
},
"annotations": {
"grafana.app/createdBy": "user:cejvsh18uudxcf",
"grafana.app/updatedBy": "user:cejvsh18uudxcf",
"grafana.app/updatedTimestamp": "2025-05-16T09:41:56Z",
"grafana.app/folder": ""
}
},
"spec": {
"annotations": [
@ -14,20 +24,19 @@
"kind": "AnnotationQuery",
"spec": {
"builtIn": true,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"query": {
"group": "grafana",
"kind": "DataQuery",
"spec": {},
"version": "v0"
}
"name": "Annotations & Alerts"
}
}
],
"cursorSync": "Off",
"description": "",
"editable": true,
"elements": {
"panel-1": {
@ -40,12 +49,14 @@
{
"kind": "PanelQuery",
"spec": {
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"hidden": false,
"query": {
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {},
"version": "v0"
"kind": "grafana-testdata-datasource",
"spec": {}
},
"refId": "A"
}
@ -144,12 +155,14 @@
{
"kind": "PanelQuery",
"spec": {
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"hidden": false,
"query": {
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {},
"version": "v0"
"kind": "grafana-testdata-datasource",
"spec": {}
},
"refId": "A"
}
@ -248,12 +261,14 @@
{
"kind": "PanelQuery",
"spec": {
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"hidden": false,
"query": {
"group": "grafana-testdata-datasource",
"kind": "DataQuery",
"spec": {},
"version": "v0"
"kind": "grafana-testdata-datasource",
"spec": {}
},
"refId": "A"
}
@ -365,7 +380,7 @@
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-3"
"name": "panel-2"
},
"height": 8,
"width": 12,
@ -378,7 +393,7 @@
"spec": {
"element": {
"kind": "ElementReference",
"name": "panel-2"
"name": "panel-3"
},
"height": 8,
"width": 12,
@ -402,7 +417,7 @@
"timezone": "browser",
"to": "now"
},
"title": "Test V2 Dashboard",
"title": "New Test V2 Dashboard",
"variables": []
},
"status": {}

@ -17,9 +17,5 @@ export const importV2Dashboard = ({ title }: ImportDashboardConfig) => {
if (title) {
e2e.components.ImportDashboardForm.name().clear().type(title);
}
e2e.components.DataSourcePicker.inputV2().click();
cy.get('div[data-testid="data-source-card"]').first().click();
e2e.components.ImportDashboardForm.submit().click();
};

@ -347,6 +347,7 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
github.com/RaveNoX/go-jsoncommentstrip v1.0.0 h1:t527LHHE3HmiHrq74QMpNPZpGCIJzTx+apLkMKt4HC0=
github.com/RoaringBitmap/gocroaring v0.4.0 h1:5nufXUgWpBEUNEJXw7926YAA58ZAQRpWPrQV1xCoSjc=
github.com/RoaringBitmap/real-roaring-datasets v0.0.0-20190726190000-eb7c87156f76 h1:ZYlhPbqQFU+AHfgtCdHGDTtRW1a8geZyiE8c6Q+Sl1s=
github.com/RoaringBitmap/real-roaring-datasets v0.0.0-20190726190000-eb7c87156f76/go.mod h1:oM0MHmQ3nDsq609SS36p+oYbRi16+oVvU2Bw4Ipv0SE=
github.com/Sereal/Sereal/Go/sereal v0.0.0-20231009093132-b9187f1a92c6 h1:5kUcJJAKWWI82Xnp/CaU0eu5hLlHkmm9acjowSkwCd0=
github.com/Sereal/Sereal/Go/sereal v0.0.0-20231009093132-b9187f1a92c6/go.mod h1:JwrycNnC8+sZPDyzM3MQ86LvaGzSpfxg885KOOwFRW4=
github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0=
@ -570,6 +571,7 @@ github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQ
github.com/envoyproxy/go-control-plane/envoy v1.32.3/go.mod h1:F6hWupPfh75TBXGKA++MCT/CZHFq5r9/uwt/kQYkZfE=
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/expr-lang/expr v1.17.0/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
@ -679,6 +681,8 @@ github.com/grafana/alerting v0.0.0-20250403153742-418bc7118d05 h1:hMzOzI/S0nkZt0
github.com/grafana/alerting v0.0.0-20250403153742-418bc7118d05/go.mod h1:K3YAJumchx5EEZItGv4D3pCv/Ux796hmoOibP/p/eYk=
github.com/grafana/alerting v0.0.0-20250429131604-de176b4a0309 h1:H2p3XKDHnTBGkMXLCgXiqb2dFnHbQ4zPDXOwKK4Ne3Y=
github.com/grafana/alerting v0.0.0-20250429131604-de176b4a0309/go.mod h1:pMfhRxL2LZ3Pm8iy7VcVsb9CLYuBtjFYbf1oxgx7yFA=
github.com/grafana/alerting v0.0.0-20250701210250-cea2d1683945 h1:3imTbxFpZSVI6IBIB9mn+Xc40lUweWjfMaBSgXR7rLs=
github.com/grafana/alerting v0.0.0-20250701210250-cea2d1683945/go.mod h1:gtR7agmxVfJOmNKV/n2ZULgOYTYNL+PDKYB5N48tQ7Q=
github.com/grafana/authlib v0.0.0-20250123104008-e99947858901/go.mod h1:/gYfphsNu9v1qYWXxpv1NSvMEMSwvdf8qb8YlgwIRl8=
github.com/grafana/authlib/types v0.0.0-20250120144156-d6737a7dc8f5/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM=
github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM=
@ -693,6 +697,8 @@ github.com/grafana/grafana-app-sdk/logging v0.39.0 h1:3GgN5+dUZYqq74Q+GT9/ET+yo+
github.com/grafana/grafana-app-sdk/logging v0.39.0/go.mod h1:WhDENSnaGHtyVVwZGVnAR7YLvh2xlLDYR3D7E6h7XVk=
github.com/grafana/grafana-aws-sdk v0.38.2 h1:TzQD0OpWsNjtldi5G5TLDlBRk8OyDf+B5ujcoAu4Dp0=
github.com/grafana/grafana-aws-sdk v0.38.2/go.mod h1:j3vi+cXYHEFqjhBGrI6/lw1TNM+dl0Y3f0cSnDOPy+s=
github.com/grafana/grafana-aws-sdk v1.0.2 h1:98eBuHYFmgvH0xO9kKf4RBsEsgQRp8EOA/9yhDIpkss=
github.com/grafana/grafana-aws-sdk v1.0.2/go.mod h1:hO7q7yWV+t6dmiyJjMa3IbuYnYkBua+G/IAlOPVIYKE=
github.com/grafana/grafana-plugin-sdk-go v0.263.0/go.mod h1:U43Cnrj/9DNYyvFcNdeUWNjMXTKNB0jcTcQGpWKd2gw=
github.com/grafana/grafana-plugin-sdk-go v0.267.0/go.mod h1:OuwS4c/JYgn0rr/w5zhJBpLo4gKm/vw15RsfpYAvK9Q=
github.com/grafana/grafana-plugin-sdk-go v0.269.1/go.mod h1:yv2KbO4mlr9WuDK2f+2gHAMTwwLmLuqaEnrPXTRU+OI=
@ -1270,6 +1276,7 @@ golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhp
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
golang.org/x/mod v0.6.0-dev.0.20220818022119-ed83ed61efb9/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -1297,6 +1304,7 @@ golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
gonum.org/v1/plot v0.15.2 h1:Tlfh/jBk2tqjLZ4/P8ZIwGrLEWQSPDLRm/SNWKNXiGI=
gonum.org/v1/plot v0.15.2/go.mod h1:DX+x+DWso3LTha+AdkJEv5Txvi+Tql3KAGkehP0/Ubg=
@ -1382,6 +1390,7 @@ k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7 h1:2OX19X59HxDprNCVrWi6jb7LW1
k8s.io/gengo/v2 v2.0.0-20250207200755-1244d31929d7/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kms v0.33.2/go.mod h1:C1I8mjFFBNzfUZXYt9FZVJ8MJl7ynFbGgZFbBzkBJ3E=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg=
modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM=

@ -1,4 +1,4 @@
import { defaultDataQueryKind, Spec } from './v2alpha1/types.spec.gen';
import { Spec } from './v2alpha1/types.spec.gen';
export const handyTestingSchema: Spec = {
title: 'Default Dashboard',
@ -37,16 +37,15 @@ export const handyTestingSchema: Spec = {
spec: {
builtIn: false,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
datasource: {
name: 'uid',
},
kind: 'prometheus',
spec: {
expr: 'test-query',
},
},
datasource: {
type: 'prometheus',
uid: 'uid',
},
filter: { ids: [1] },
enable: true,
hide: false,
@ -58,16 +57,15 @@ export const handyTestingSchema: Spec = {
kind: 'AnnotationQuery',
spec: {
builtIn: false,
datasource: {
type: 'grafana-testdata-datasource',
uid: 'uid',
},
enable: true,
iconColor: 'red',
name: 'Enabled',
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'grafana-testdata-datasource',
datasource: {
name: 'uid',
},
kind: 'grafana-testdata-datasource',
spec: {
lines: 4,
refId: 'Anno',
@ -81,16 +79,15 @@ export const handyTestingSchema: Spec = {
kind: 'AnnotationQuery',
spec: {
builtIn: false,
datasource: {
type: 'grafana-testdata-datasource',
uid: 'uid',
},
enable: false,
iconColor: 'yellow',
name: 'Disabled',
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'grafana-testdata-datasource',
datasource: {
name: 'uid',
},
kind: 'grafana-testdata-datasource',
spec: { lines: 5, refId: 'Anno', scenarioId: 'annotations' },
},
hide: false,
@ -100,17 +97,16 @@ export const handyTestingSchema: Spec = {
kind: 'AnnotationQuery',
spec: {
builtIn: false,
datasource: {
type: 'grafana-testdata-datasource',
uid: 'uid',
},
enable: true,
hide: true,
iconColor: 'dark-purple',
name: 'Hidden',
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'grafana-testdata-datasource',
datasource: {
name: 'uid',
},
kind: 'grafana-testdata-datasource',
spec: {
lines: 6,
refId: 'Anno',
@ -132,13 +128,12 @@ export const handyTestingSchema: Spec = {
kind: 'PanelQuery',
spec: {
refId: 'A',
datasource: {
type: 'prometheus',
uid: 'datasource1',
},
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
datasource: {
name: 'datasource1',
},
kind: 'prometheus',
spec: {
expr: 'test-query',
},
@ -270,6 +265,10 @@ export const handyTestingSchema: Spec = {
text: 'text1',
value: 'value1',
},
datasource: {
type: 'prometheus',
uid: 'datasource1',
},
definition: 'definition1',
description: 'A query variable',
hide: 'dontHide',
@ -279,12 +278,7 @@ export const handyTestingSchema: Spec = {
name: 'queryVar',
options: [],
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
datasource: {
name: 'datasource1',
},
kind: 'prometheus',
spec: {
expr: 'test-query',
refId: 'A',

@ -11,7 +11,8 @@ export const defaultAnnotationQueryKind = (): AnnotationQueryKind => ({
});
export interface AnnotationQuerySpec {
query: DataQueryKind;
datasource?: DataSourceRef;
query?: DataQueryKind;
enable: boolean;
hide: boolean;
iconColor: string;
@ -23,7 +24,6 @@ export interface AnnotationQuerySpec {
}
export const defaultAnnotationQuerySpec = (): AnnotationQuerySpec => ({
query: defaultDataQueryKind(),
enable: false,
hide: false,
iconColor: "",
@ -31,22 +31,24 @@ export const defaultAnnotationQuerySpec = (): AnnotationQuerySpec => ({
builtIn: false,
});
export interface DataSourceRef {
// The plugin type-id
type?: string;
// Specific datasource instance
uid?: string;
}
export const defaultDataSourceRef = (): DataSourceRef => ({
});
export interface DataQueryKind {
kind: "DataQuery";
group: string;
version: string;
// New type for datasource reference
// Not creating a new type until we figure out how to handle DS refs for group by, adhoc, and every place that uses DataSourceRef in TS.
datasource?: {
name?: string;
};
// The kind of a DataQueryKind is the datasource type
kind: string;
spec: Record<string, any>;
}
export const defaultDataQueryKind = (): DataQueryKind => ({
kind: "DataQuery",
group: "",
version: "v0",
kind: "",
spec: {},
});
@ -149,6 +151,7 @@ export const defaultPanelQueryKind = (): PanelQueryKind => ({
export interface PanelQuerySpec {
query: DataQueryKind;
datasource?: DataSourceRef;
refId: string;
hidden: boolean;
}
@ -991,6 +994,7 @@ export interface QueryVariableSpec {
refresh: VariableRefresh;
skipUrlSync: boolean;
description?: string;
datasource?: DataSourceRef;
query: DataQueryKind;
regex: string;
sort: VariableSort;
@ -1279,18 +1283,6 @@ export const defaultGroupByVariableSpec = (): GroupByVariableSpec => ({
skipUrlSync: false,
});
// Keeping this for backwards compatibility for GroupByVariableSpec and AdhocVariableSpec
// This type is widely used in the codebase and changing it will have a big impact
export interface DataSourceRef {
// The plugin type-id
type?: string;
// Specific datasource instance
uid?: string;
}
export const defaultDataSourceRef = (): DataSourceRef => ({
});
// Adhoc variable kind
export interface AdhocVariableKind {
kind: "AdhocVariable";

@ -1250,7 +1250,6 @@
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationQuerySpec": {
"type": "object",
"required": [
"query",
"enable",
"hide",
"iconColor",
@ -1260,6 +1259,9 @@
"builtIn": {
"type": "boolean"
},
"datasource": {
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataSourceRef"
},
"enable": {
"type": "boolean",
"default": false
@ -1287,12 +1289,7 @@
"default": ""
},
"query": {
"default": {},
"allOf": [
{
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataQueryKind"
}
]
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataQueryKind"
}
}
},
@ -1868,24 +1865,11 @@
"type": "object",
"required": [
"kind",
"group",
"version",
"spec"
],
"properties": {
"datasource": {
"description": "New type for datasource reference Not creating a new type until we figure out how to handle DS refs for group by, adhoc, and every place that uses DataSourceRef in TS.",
"allOf": [
{
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1DataQueryKindDatasource"
}
]
},
"group": {
"type": "string",
"default": ""
},
"kind": {
"description": "The kind of a DataQueryKind is the datasource type",
"type": "string",
"default": ""
},
@ -1894,15 +1878,10 @@
"additionalProperties": {
"type": "object"
}
},
"version": {
"type": "string",
"default": ""
}
}
},
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataSourceRef": {
"description": "Keeping this for backwards compatibility for GroupByVariableSpec and AdhocVariableSpec This type is widely used in the codebase and changing it will have a big impact",
"type": "object",
"properties": {
"type": {
@ -2764,6 +2743,9 @@
"hidden"
],
"properties": {
"datasource": {
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataSourceRef"
},
"hidden": {
"type": "boolean",
"default": false
@ -3011,6 +2993,9 @@
}
]
},
"datasource": {
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataSourceRef"
},
"definition": {
"type": "string"
},
@ -3740,14 +3725,6 @@
}
}
},
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1DataQueryKindDatasource": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1FieldConfigSourceOverrides": {
"type": "object",
"required": [

@ -37,39 +37,6 @@ jest.mock('@grafana/runtime', () => {
...original.config.featureToggles,
dashboardNewLayouts: false, // Default value
},
bootData: {
...original.config.bootData,
settings: {
...original.config.bootData.settings,
datasources: {
'gdev-testdata': {
id: 7,
uid: 'abc',
type: 'grafana-testdata-datasource',
name: 'gdev-testdata',
meta: {
id: 'grafana-testdata-datasource',
type: 'datasource',
name: 'TestData',
aliasIDs: ['testdata'],
},
},
'-- Grafana --': {
id: -1,
uid: 'grafana',
type: 'datasource',
name: '-- Grafana --',
meta: {
id: 'grafana',
type: 'datasource',
name: '-- Grafana --',
},
},
},
defaultDatasource: 'gdev-testdata',
},
},
},
};
});
@ -1621,11 +1588,9 @@ const v2ProvisionedDashboardResource = {
kind: 'AnnotationQuery',
spec: {
builtIn: true,
query: {
kind: 'DataQuery',
group: 'grafana',
spec: {},
version: 'v0',
datasource: {
type: 'grafana',
uid: '-- Grafana --',
},
enable: true,
hide: true,
@ -1648,14 +1613,13 @@ const v2ProvisionedDashboardResource = {
{
kind: 'PanelQuery',
spec: {
datasource: {
type: 'grafana-testdata-datasource',
uid: 'PD8C576611E62080A',
},
hidden: false,
query: {
kind: 'DataQuery',
group: 'grafana-testdata-datasource',
version: 'v0',
datasource: {
name: 'PD8C576611E62080A',
},
kind: 'grafana-testdata-datasource',
spec: {
scenarioId: 'random_walk',
seriesCount: 2,

@ -549,12 +549,13 @@ describe('dashboard exporter v2', () => {
{
kind: 'PanelQuery',
spec: {
datasource: {
type: 'prometheus',
uid: '${datasourceVar}',
},
hidden: false,
query: {
datasource: {
name: '${datasourceVar}',
},
group: 'prometheus',
kind: 'prometheus',
spec: {
editorMode: 'builder',
expr: 'go_goroutines{job="prometheus"}',
@ -582,7 +583,7 @@ describe('dashboard exporter v2', () => {
it('should replace datasource in a query variable', async () => {
const { dashboard } = await setup();
const variable = dashboard.variables[0] as QueryVariableKind;
expect(variable.spec.query.datasource?.name).toBeUndefined();
expect(variable.spec.datasource?.uid).toBeUndefined();
});
it('do not expose datasource name and id in datasource variable', async () => {
@ -596,7 +597,7 @@ describe('dashboard exporter v2', () => {
const { dashboard } = await setup();
const annotationQuery = dashboard.annotations[0];
expect(annotationQuery.spec.query?.datasource?.name).toBeUndefined();
expect(annotationQuery.spec.datasource?.uid).toBeUndefined();
});
it('should remove library panels from layout', async () => {
@ -615,8 +616,11 @@ describe('dashboard exporter v2', () => {
if (panel.kind !== 'Panel') {
throw new Error('Panel should be a Panel');
}
expect(panel.spec.data.spec.queries[0].spec.query.datasource?.name).toBe('${datasourceVar}');
expect(panel.spec.data.spec.queries[0].spec.query.group).toBe('prometheus');
expect(panel.spec.data.spec.queries[0].spec.datasource).toEqual({
type: 'prometheus',
uid: '${datasourceVar}',
});
});
});

@ -344,8 +344,7 @@ export async function makeExportableV2(dashboard: DashboardV2Spec) {
const removeDataSourceRefs = (
obj: AnnotationQueryKind['spec'] | QueryVariableKind['spec'] | PanelQueryKind['spec']
) => {
const datasourceUid = obj.query?.datasource?.name;
const datasourceUid = obj.datasource?.uid;
if (datasourceUid?.startsWith('${') && datasourceUid?.endsWith('}')) {
const varName = datasourceUid.slice(2, -1);
// if there's a match we don't want to remove the datasource ref
@ -355,7 +354,7 @@ export async function makeExportableV2(dashboard: DashboardV2Spec) {
}
}
obj.query && (obj.query.datasource = undefined);
obj.datasource = undefined;
};
const processPanel = (panel: PanelKind) => {

@ -10,7 +10,6 @@ import { Dashboard, VariableModel } from '@grafana/schema';
import {
Spec as DashboardV2Spec,
defaultSpec as defaultDashboardV2Spec,
defaultDataQueryKind,
defaultPanelSpec,
defaultTimeSettingsSpec,
GridLayoutKind,
@ -724,14 +723,9 @@ describe('DashboardSceneSerializer', () => {
spec: {
builtIn: true,
name: 'Annotations & Alerts',
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'grafana',
datasource: {
name: '-- Grafana --',
},
spec: {},
datasource: {
uid: '-- Grafana --',
type: 'grafana',
},
enable: true,
hide: true,
@ -1020,7 +1014,7 @@ describe('DashboardSceneSerializer', () => {
refId: 'A',
hidden: false,
// No datasource defined
query: { kind: 'DataQuery', version: defaultDataQueryKind().version, group: 'sql', spec: {} },
query: { kind: 'sql', spec: {} },
},
},
{
@ -1028,15 +1022,8 @@ describe('DashboardSceneSerializer', () => {
spec: {
refId: 'B',
hidden: false,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
datasource: {
name: 'datasource-1',
},
spec: {},
},
datasource: { uid: 'datasource-1', type: 'prometheus' },
query: { kind: 'prometheus', spec: {} },
},
},
],
@ -1071,7 +1058,7 @@ describe('DashboardSceneSerializer', () => {
refId: 'C',
hidden: false,
// No datasource defined
query: { kind: 'DataQuery', version: defaultDataQueryKind().version, group: 'sql', spec: {} },
query: { kind: 'sql', spec: {} },
},
},
],
@ -1119,7 +1106,7 @@ describe('DashboardSceneSerializer', () => {
kind: 'AnnotationQuery',
spec: {
name: 'Annotation 1',
query: { kind: 'DataQuery', version: defaultDataQueryKind().version, group: 'prometheus', spec: {} },
query: { kind: 'prometheus', spec: {} },
enable: true,
hide: false,
iconColor: 'red',

@ -288,7 +288,7 @@ export class V2DashboardSerializer
const panelQueries = elementPanel.spec.data.spec.queries;
for (const query of panelQueries) {
if (!query.spec.query.datasource?.name) {
if (!query.spec.datasource) {
const elementId = this.getElementIdForPanel(elementPanel.spec.id);
if (!this.defaultDsReferencesMap.panels.has(elementId)) {
this.defaultDsReferencesMap.panels.set(elementId, new Set());
@ -306,7 +306,7 @@ export class V2DashboardSerializer
if (saveModel?.variables) {
for (const variable of saveModel.variables) {
// for query variables that dont have a ds defined add them to the list
if (variable.kind === 'QueryVariable' && !variable.spec.query.datasource?.name) {
if (variable.kind === 'QueryVariable' && !variable.spec.datasource) {
this.defaultDsReferencesMap.variables.add(variable.spec.name);
}
}
@ -315,7 +315,7 @@ export class V2DashboardSerializer
// initialize annotations ds references map
if (saveModel?.annotations) {
for (const annotation of saveModel.annotations) {
if (!annotation.spec.query?.datasource?.name) {
if (!annotation.spec.datasource) {
this.defaultDsReferencesMap.annotations.add(annotation.spec.name);
}
}

@ -7,38 +7,38 @@ exports[`transformSceneToSaveModelSchemaV2 should transform scene to save model
"kind": "AnnotationQuery",
"spec": {
"builtIn": false,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --",
},
"enable": true,
"hide": false,
"iconColor": "red",
"name": "query1",
"query": {
"datasource": {
"name": "-- Grafana --",
},
"group": "grafana",
"kind": "DataQuery",
"spec": {},
"version": "v0",
},
},
},
{
"kind": "AnnotationQuery",
"spec": {
"builtIn": false,
"datasource": {
"type": "prometheus",
"uid": "abcdef",
},
"enable": true,
"hide": true,
"iconColor": "blue",
"name": "query2",
"query": {
"datasource": {
"name": "abcdef",
},
"group": "prometheus",
"kind": "DataQuery",
"spec": {},
"version": "v0",
},
},
},
{
"kind": "AnnotationQuery",
"spec": {
"builtIn": false,
"enable": true,
"hide": true,
"iconColor": "green",
"name": "query3",
},
},
],
@ -152,6 +152,10 @@ exports[`transformSceneToSaveModelSchemaV2 should transform scene to save model
"text": "text1",
"value": "value1",
},
"datasource": {
"type": "prometheus",
"uid": "datasource1",
},
"definition": "definition1",
"description": "A query variable",
"hide": "hideLabel",
@ -161,16 +165,11 @@ exports[`transformSceneToSaveModelSchemaV2 should transform scene to save model
"name": "queryVar",
"options": [],
"query": {
"datasource": {
"name": "datasource1",
},
"group": "prometheus",
"kind": "DataQuery",
"kind": "prometheus",
"spec": {
"expr": "label_values(node_boot_time_seconds)",
"refId": "A",
},
"version": "v0",
},
"refresh": "onDashboardLoad",
"regex": "regex1",

@ -1,7 +1,4 @@
import {
defaultDataQueryKind,
PanelQueryKind,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
import { PanelQueryKind } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
import { getRuntimePanelDataSource } from './utils';
@ -32,12 +29,6 @@ jest.mock('@grafana/runtime', () => ({
meta: { id: 'loki' },
type: 'datasource',
},
'-- Grafana --': {
uid: 'grafana',
name: 'Grafana',
meta: { id: 'grafana' },
type: 'datasource',
},
},
},
},
@ -51,23 +42,22 @@ describe('getRuntimePanelDataSource', () => {
spec: {
refId: 'A',
hidden: false,
datasource: {
uid: 'test-ds-uid',
type: 'test-ds-type',
},
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
datasource: {
name: 'prometheus-uid',
},
kind: 'prometheus',
spec: {},
},
},
};
const result = getRuntimePanelDataSource(query.spec.query);
const result = getRuntimePanelDataSource(query);
expect(result).toEqual({
uid: 'prometheus-uid',
type: 'prometheus',
uid: 'test-ds-uid',
type: 'test-ds-type',
});
});
@ -77,16 +67,15 @@ describe('getRuntimePanelDataSource', () => {
spec: {
refId: 'A',
hidden: false,
datasource: undefined,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
kind: 'prometheus',
spec: {},
},
},
};
const result = getRuntimePanelDataSource(query.spec.query);
const result = getRuntimePanelDataSource(query);
expect(result).toEqual({
uid: 'default-prometheus-uid',
@ -100,16 +89,15 @@ describe('getRuntimePanelDataSource', () => {
spec: {
refId: 'A',
hidden: false,
datasource: undefined,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'loki',
kind: 'loki',
spec: {},
},
},
};
const result = getRuntimePanelDataSource(query.spec.query);
const result = getRuntimePanelDataSource(query);
expect(result).toEqual({
uid: 'loki-uid',
@ -124,16 +112,15 @@ describe('getRuntimePanelDataSource', () => {
spec: {
refId: 'A',
hidden: false,
datasource: undefined,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'unknown-type',
kind: 'unknown-type',
spec: {},
},
},
};
const result = getRuntimePanelDataSource(query.spec.query);
const result = getRuntimePanelDataSource(query);
expect(result).toEqual({
uid: 'default-prometheus-uid',
@ -151,19 +138,18 @@ describe('getRuntimePanelDataSource', () => {
spec: {
refId: 'A',
hidden: false,
datasource: {
uid: '',
type: 'test-ds-type',
},
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
datasource: {
name: '',
},
kind: 'prometheus',
spec: {},
},
},
};
const result = getRuntimePanelDataSource(query.spec.query);
const result = getRuntimePanelDataSource(query);
expect(result).toEqual({
uid: 'default-prometheus-uid',

@ -19,7 +19,6 @@ import {
PanelQueryKind,
QueryVariableKind,
TabsLayoutTabKind,
DataQueryKind,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource';
@ -186,15 +185,12 @@ function getPanelDataSource(panel: PanelKind): DataSourceRef | undefined {
panel.spec.data.spec.queries.forEach((query) => {
if (!datasource) {
if (!query.spec.query.datasource?.name) {
datasource = getRuntimePanelDataSource(query.spec.query);
if (!query.spec.datasource?.uid) {
datasource = getRuntimePanelDataSource(query);
} else {
datasource = {
uid: query.spec.query.datasource?.name,
type: query.spec.query.group,
};
datasource = query.spec.datasource;
}
} else if (datasource.uid !== query.spec.query.datasource?.name || datasource.type !== query.spec.query.group) {
} else if (datasource.uid !== query.spec.datasource?.uid || datasource.type !== query.spec.datasource?.type) {
isMixedDatasource = true;
}
});
@ -203,19 +199,11 @@ function getPanelDataSource(panel: PanelKind): DataSourceRef | undefined {
}
export function getRuntimeVariableDataSource(variable: QueryVariableKind): DataSourceRef | undefined {
const ds: DataSourceRef = {
uid: variable.spec.query.datasource?.name,
type: variable.spec.query.group,
};
return getDataSourceForQuery(ds, variable.spec.query.group);
return getDataSourceForQuery(variable.spec.datasource, variable.spec.query.kind);
}
export function getRuntimePanelDataSource(query: DataQueryKind): DataSourceRef {
const ds: DataSourceRef = {
uid: query.datasource?.name,
type: query.group,
};
return getDataSourceForQuery(ds, query.group);
export function getRuntimePanelDataSource(query: PanelQueryKind): DataSourceRef | undefined {
return getDataSourceForQuery(query.spec.datasource, query.spec.query.kind);
}
/**
@ -223,7 +211,10 @@ export function getRuntimePanelDataSource(query: DataQueryKind): DataSourceRef {
* @param queryKind - The kind of query being performed
* @returns The resolved DataSourceRef
*/
function getDataSourceForQuery(querySpecDS: DataSourceRef | undefined | null, queryKind: string): DataSourceRef {
function getDataSourceForQuery(
querySpecDS: DataSourceRef | undefined | null,
queryKind: string
): DataSourceRef | undefined {
// If datasource is specified and has a uid, use it
if (querySpecDS?.uid) {
return querySpecDS;
@ -262,22 +253,14 @@ function getDataSourceForQuery(querySpecDS: DataSourceRef | undefined | null, qu
};
}
if (dsList && !dsList[defaultDatasource]) {
throw new Error(`Default datasource ${defaultDatasource} not found in datasource list`);
}
// In the datasource list from bootData "id" is the type and the uid could be uid or the name
// in cases like grafana, dashboard or mixed datasource
return {
uid: dsList[defaultDatasource].uid || dsList[defaultDatasource].name,
type: dsList[defaultDatasource].meta.id,
};
// If we don't find a default datasource, return undefined
return undefined;
}
function panelQueryKindToSceneQuery(query: PanelQueryKind): SceneDataQuery {
return {
refId: query.spec.refId,
datasource: getRuntimePanelDataSource(query.spec.query),
datasource: getRuntimePanelDataSource(query),
hide: query.spec.hidden,
...query.spec.query.spec,
};

@ -45,8 +45,8 @@ const getDataSourceMock = jest.fn();
const fakeDsMock: DataSourceApi = {
name: 'fake-std',
type: 'fake-type',
getRef: () => ({ type: 'fake-type', uid: 'fake-uid' }),
type: 'fake-std',
getRef: () => ({ type: 'fake-std', uid: 'fake-std' }),
query: () =>
Promise.resolve({
data: [],
@ -74,7 +74,7 @@ const fakeDsMock: DataSourceApi = {
toDataQuery: (q) => ({ ...q, refId: 'FakeDataSource-refId' }),
},
id: 1,
uid: 'fake-uid',
uid: 'fake-std',
};
jest.mock('@grafana/runtime', () => ({
@ -95,7 +95,7 @@ describe('sceneVariablesSetToVariables', () => {
description: 'test-desc',
value: ['selected-value'],
text: ['selected-value-text'],
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
query: 'query',
includeAll: true,
allowCustomValue: true,
@ -123,8 +123,8 @@ describe('sceneVariablesSetToVariables', () => {
],
},
"datasource": {
"type": "fake-type",
"uid": "fake-uid",
"type": "fake-std",
"uid": "fake-std",
},
"definition": undefined,
"description": "test-desc",
@ -148,7 +148,7 @@ describe('sceneVariablesSetToVariables', () => {
description: 'test-desc',
value: ['selected-value'],
text: ['selected-value-text'],
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
query: 'query',
definition: 'query',
includeAll: true,
@ -176,8 +176,8 @@ describe('sceneVariablesSetToVariables', () => {
],
},
"datasource": {
"type": "fake-type",
"uid": "fake-uid",
"type": "fake-std",
"uid": "fake-std",
},
"definition": "query",
"description": "test-desc",
@ -201,7 +201,7 @@ describe('sceneVariablesSetToVariables', () => {
description: 'test-desc',
value: ['selected-value'],
text: ['selected-value-text'],
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
query: 'query',
options: [
{ label: 'test', value: 'test' },
@ -228,7 +228,7 @@ describe('sceneVariablesSetToVariables', () => {
description: 'test-desc',
value: ['test'],
text: ['test'],
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
query: 'query',
options: [
{ label: 'test', value: 'test' },
@ -499,7 +499,7 @@ describe('sceneVariablesSetToVariables', () => {
allowCustomValue: true,
label: 'test-label',
description: 'test-desc',
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
filters: [
{
key: 'filterTest',
@ -533,8 +533,8 @@ describe('sceneVariablesSetToVariables', () => {
},
],
"datasource": {
"type": "fake-type",
"uid": "fake-uid",
"type": "fake-std",
"uid": "fake-std",
},
"defaultKeys": undefined,
"description": "test-desc",
@ -662,7 +662,7 @@ describe('sceneVariablesSetToVariables', () => {
allowCustomValue: true,
label: 'test-label',
description: 'test-desc',
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
defaultKeys: [
{
text: 'some',
@ -710,8 +710,8 @@ describe('sceneVariablesSetToVariables', () => {
},
],
"datasource": {
"type": "fake-type",
"uid": "fake-uid",
"type": "fake-std",
"uid": "fake-std",
},
"defaultKeys": [
{
@ -757,7 +757,7 @@ describe('sceneVariablesSetToVariables', () => {
label: 'test-label',
description: 'test-desc',
allowCustomValue: true,
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
defaultOptions: [
{
text: 'Foo',
@ -784,8 +784,8 @@ describe('sceneVariablesSetToVariables', () => {
"value": [],
},
"datasource": {
"type": "fake-type",
"uid": "fake-uid",
"type": "fake-std",
"uid": "fake-std",
},
"defaultValue": undefined,
"description": "test-desc",
@ -813,7 +813,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
defaultOptions: [
{
text: 'Foo',
@ -841,7 +841,7 @@ describe('sceneVariablesSetToVariables', () => {
description: 'test-desc',
value: ['selected-value'],
text: ['selected-value-text'],
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
query: 'query',
includeAll: true,
allValue: 'test-all',
@ -856,45 +856,44 @@ describe('sceneVariablesSetToVariables', () => {
expect(result).toHaveLength(1);
expect(result[0]).toMatchInlineSnapshot(`
{
"kind": "QueryVariable",
{
"kind": "QueryVariable",
"spec": {
"allValue": "test-all",
"allowCustomValue": true,
"current": {
"text": [
"selected-value-text",
],
"value": [
"selected-value",
],
},
"datasource": {
"type": "fake-std",
"uid": "fake-std",
},
"definition": undefined,
"description": "test-desc",
"hide": "dontHide",
"includeAll": true,
"label": "test-label",
"multi": true,
"name": "test",
"options": [],
"query": {
"kind": "fake-std",
"spec": {
"allValue": "test-all",
"allowCustomValue": true,
"current": {
"text": [
"selected-value-text",
],
"value": [
"selected-value",
],
},
"definition": undefined,
"description": "test-desc",
"hide": "dontHide",
"includeAll": true,
"label": "test-label",
"multi": true,
"name": "test",
"options": [],
"query": {
"datasource": {
"name": "fake-uid",
},
"group": "fake-type",
"kind": "DataQuery",
"spec": {
"__legacyStringValue": "query",
},
"version": "v0",
},
"refresh": "onDashboardLoad",
"regex": "",
"skipUrlSync": false,
"sort": "disabled",
"__legacyStringValue": "query",
},
}
`);
},
"refresh": "onDashboardLoad",
"regex": "",
"skipUrlSync": false,
"sort": "disabled",
},
}
`);
});
it('should handle CustomVariable', () => {
@ -1153,7 +1152,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
filters: [
{
key: 'filterTest',
@ -1189,8 +1188,8 @@ describe('sceneVariablesSetToVariables', () => {
},
],
"datasource": {
"type": "fake-type",
"uid": "fake-uid",
"type": "fake-std",
"uid": "fake-std",
},
"defaultKeys": [],
"description": "test-desc",
@ -1215,7 +1214,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
defaultKeys: [
{
text: 'some',
@ -1265,8 +1264,8 @@ describe('sceneVariablesSetToVariables', () => {
},
],
"datasource": {
"type": "fake-type",
"uid": "fake-uid",
"type": "fake-std",
"uid": "fake-std",
},
"defaultKeys": [
{
@ -1313,7 +1312,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
defaultOptions: [
{
text: 'Foo',
@ -1341,8 +1340,8 @@ describe('sceneVariablesSetToVariables', () => {
"value": [],
},
"datasource": {
"type": "fake-type",
"uid": "fake-uid",
"type": "fake-std",
"uid": "fake-std",
},
"defaultValue": undefined,
"description": "test-desc",
@ -1373,7 +1372,7 @@ describe('sceneVariablesSetToVariables', () => {
name: 'test',
label: 'test-label',
description: 'test-desc',
datasource: { uid: 'fake-uid', type: 'fake-type' },
datasource: { uid: 'fake-std', type: 'fake-std' },
defaultOptions: [
{
text: 'Foo',

@ -24,7 +24,6 @@ import {
GroupByVariableKind,
defaultVariableHide,
VariableOption,
defaultDataQueryKind,
AdHocFilterWithLabels,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
@ -295,30 +294,14 @@ export function sceneVariablesSetToSchemaV2Variables(
}
const query = variable.state.query;
let dataQuery: DataQueryKind | string;
const datasource = getElementDatasource(set, variable, 'variable', undefined, dsReferencesMapping);
if (typeof query !== 'string') {
dataQuery = {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: datasource?.type ?? getDataQueryKind(query),
...(datasource?.uid && {
datasource: {
name: datasource.uid,
},
}),
kind: variable.state.datasource?.type ?? getDataQueryKind(query),
spec: getDataQuerySpec(query),
};
} else {
dataQuery = {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: datasource?.type ?? getDataQueryKind(query),
...(datasource?.uid && {
datasource: {
name: datasource.uid,
},
}),
kind: variable.state.datasource?.type ?? getDataQueryKind(query),
spec: {
[LEGACY_STRING_VALUE_KEY]: query,
},
@ -332,6 +315,7 @@ export function sceneVariablesSetToSchemaV2Variables(
options,
query: dataQuery,
definition: variable.state.definition,
datasource: getElementDatasource(set, variable, 'variable', undefined, dsReferencesMapping),
sort: transformSortVariableToEnum(variable.state.sort),
refresh: transformVariableRefreshToEnum(variable.state.refresh),
regex: variable.state.regex,

@ -22,7 +22,6 @@ import {
CustomVariableKind,
Spec as DashboardV2Spec,
DatasourceVariableKind,
defaultDataQueryKind,
GridLayoutItemSpec,
GridLayoutSpec,
GroupByVariableKind,
@ -139,7 +138,6 @@ describe('transformSaveModelSchemaV2ToScene', () => {
// Variables
const variables = scene.state?.$variables;
expect(variables?.state.variables).toHaveLength(dash.variables.length);
validateVariable({
sceneVariable: variables?.state.variables[0],
variableKind: dash.variables[0] as QueryVariableKind,
@ -208,26 +206,36 @@ describe('transformSaveModelSchemaV2ToScene', () => {
// Annotations
expect(scene.state.$data).toBeInstanceOf(DashboardDataLayerSet);
const dataLayers = scene.state.$data as DashboardDataLayerSet;
// we should get two annotations, Grafana built-in and the custom ones
expect(dataLayers.state.annotationLayers).toHaveLength(dash.annotations.length);
expect(dataLayers.state.annotationLayers[0].state.name).toBe(dash.annotations[0].spec.name);
expect(dataLayers.state.annotationLayers[0].state.isEnabled).toBe(dash.annotations[0].spec.enable);
expect(dataLayers.state.annotationLayers[0].state.isHidden).toBe(dash.annotations[0].spec.hide);
expect(dataLayers.state.annotationLayers).toHaveLength(5);
// Built-in
const builtInAnnotation = dataLayers.state.annotationLayers[0] as unknown as DashboardAnnotationsDataLayer;
expect(builtInAnnotation.state.name).toBe('Annotations & Alerts');
expect(builtInAnnotation.state.isEnabled).toBe(true);
expect(builtInAnnotation.state.isHidden).toBe(true);
expect(builtInAnnotation.state?.query.builtIn).toBe(1);
// Enabled
expect(dataLayers.state.annotationLayers[1].state.name).toBe(dash.annotations[1].spec.name);
expect(dataLayers.state.annotationLayers[1].state.isEnabled).toBe(dash.annotations[1].spec.enable);
expect(dataLayers.state.annotationLayers[1].state.isHidden).toBe(dash.annotations[1].spec.hide);
// Disabled
expect(dataLayers.state.annotationLayers[2].state.name).toBe(dash.annotations[2].spec.name);
expect(dataLayers.state.annotationLayers[2].state.isEnabled).toBe(dash.annotations[2].spec.enable);
expect(dataLayers.state.annotationLayers[2].state.isHidden).toBe(dash.annotations[2].spec.hide);
// Hidden
// Disabled
expect(dataLayers.state.annotationLayers[3].state.name).toBe(dash.annotations[3].spec.name);
expect(dataLayers.state.annotationLayers[3].state.isEnabled).toBe(dash.annotations[3].spec.enable);
expect(dataLayers.state.annotationLayers[3].state.isHidden).toBe(dash.annotations[3].spec.hide);
// Hidden
expect(dataLayers.state.annotationLayers[4].state.name).toBe(dash.annotations[4].spec.name);
expect(dataLayers.state.annotationLayers[4].state.isEnabled).toBe(dash.annotations[4].spec.enable);
expect(dataLayers.state.annotationLayers[4].state.isHidden).toBe(dash.annotations[4].spec.hide);
// VizPanel
const vizPanels = (scene.state.body as DashboardLayoutManager).getVizPanels();
expect(vizPanels).toHaveLength(2);
@ -271,14 +279,13 @@ describe('transformSaveModelSchemaV2ToScene', () => {
kind: 'PanelQuery',
spec: {
refId: 'A',
datasource: {
type: 'graphite',
uid: 'datasource1',
},
hidden: false,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'graphite',
datasource: {
name: 'datasource1',
},
kind: 'prometheus',
spec: {
expr: 'test-query',
},
@ -300,14 +307,13 @@ describe('transformSaveModelSchemaV2ToScene', () => {
kind: 'PanelQuery',
spec: {
refId: 'A',
datasource: {
type: 'prometheus',
uid: 'datasource1',
},
hidden: false,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
datasource: {
name: 'datasource1',
},
kind: 'prometheus',
spec: {
expr: 'test-query',
},
@ -334,12 +340,7 @@ describe('transformSaveModelSchemaV2ToScene', () => {
refId: 'A',
hidden: false,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
datasource: {
name: 'abc123',
},
kind: 'prometheus',
spec: {
expr: 'test-query',
},
@ -721,6 +722,10 @@ describe('transformSaveModelSchemaV2ToScene', () => {
enable: true,
hide: false,
iconColor: 'purple',
datasource: {
type: 'prometheus',
uid: 'abc123',
},
legacyOptions: {
expr: 'rate(http_requests_total[5m])',
queryType: 'range',
@ -728,15 +733,6 @@ describe('transformSaveModelSchemaV2ToScene', () => {
useValueAsTime: true,
step: '1m',
},
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
datasource: {
name: 'abc123',
},
spec: {},
},
},
},
],
@ -768,36 +764,14 @@ describe('transformSaveModelSchemaV2ToScene', () => {
// Get the annotation layers
const dataLayerSet = scene.state.$data as DashboardDataLayerSet;
expect(dataLayerSet).toBeDefined();
// it should have two annotation layers, built-in and custom
expect(dataLayerSet.state.annotationLayers.length).toBe(2);
const defaultAnnotationLayer = dataLayerSet.state.annotationLayers[0] as DashboardAnnotationsDataLayer;
// Verify that the default annotation layer has been correctly initialized
expect(defaultAnnotationLayer.state.query).toEqual({
datasource: {
uid: '-- Grafana --',
type: 'grafana',
},
builtIn: 1,
enable: true,
iconColor: 'rgba(0, 211, 255, 1)',
name: 'Annotations & Alerts',
filter: undefined,
hide: true,
});
const annotationLayer = dataLayerSet.state.annotationLayers[1] as DashboardAnnotationsDataLayer;
// Verify that the legacyOptions have been merged into the query object
expect(annotationLayer.state.query).toMatchObject({
datasource: {
uid: 'abc123',
type: 'prometheus',
},
name: 'Annotation with legacy options',
builtIn: 0,
enable: true,
hide: false,
iconColor: 'purple',
expr: 'rate(http_requests_total[5m])',
queryType: 'range',
legendFormat: '{{method}} {{endpoint}}',

@ -1,6 +1,5 @@
import { uniqueId } from 'lodash';
import { AnnotationQuery } from '@grafana/data';
import { config, getDataSourceSrv } from '@grafana/runtime';
import {
AdHocFiltersVariable,
@ -29,7 +28,6 @@ import {
defaultAdhocVariableKind,
defaultConstantVariableKind,
defaultCustomVariableKind,
defaultDataQueryKind,
defaultDatasourceVariableKind,
defaultGroupByVariableKind,
defaultIntervalVariableKind,
@ -66,7 +64,7 @@ import { getIntervalsFromQueryString } from '../utils/utils';
import { SnapshotVariable } from './custom-variables/SnapshotVariable';
import { layoutDeserializerRegistry } from './layoutSerializers/layoutSerializerRegistry';
import { getRuntimePanelDataSource, getRuntimeVariableDataSource } from './layoutSerializers/utils';
import { getRuntimeVariableDataSource } from './layoutSerializers/utils';
import { registerPanelInteractionsReporter } from './transformSaveModelToScene';
import {
transformCursorSyncV2ToV1,
@ -99,46 +97,25 @@ export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo<D
}
const annotationLayers = dashboard.annotations.map((annotation) => {
let { query: dataQuery, ...annotationQuery } = annotation.spec;
// Mapping from AnnotationQueryKind to AnnotationQuery used by scenes.
let annoQuerySpec: AnnotationQuery = {
builtIn: annotation.spec.builtIn ? 1 : 0,
enable: annotation.spec.enable,
iconColor: annotation.spec.iconColor,
name: annotation.spec.name,
filter: annotation.spec.filter,
hide: annotation.spec.hide,
...dataQuery?.spec,
};
let annoQuerySpec = annotation.spec;
// some annotations will contain in the legacyOptions properties that need to be
// added to the root level annotation spec
if (annotationQuery.legacyOptions) {
if (annoQuerySpec?.legacyOptions) {
annoQuerySpec = {
...annoQuerySpec,
...annotationQuery.legacyOptions,
legacyOptions: {
...annotationQuery.legacyOptions,
},
...annoQuerySpec.legacyOptions,
};
}
// get data source from annotation query
const datasource = getRuntimePanelDataSource(dataQuery);
const layerState = {
return new DashboardAnnotationsDataLayer({
key: uniqueId('annotations-'),
query: {
...annoQuerySpec,
datasource,
builtIn: annotation.spec.builtIn ? 1 : 0,
},
name: annotation.spec.name,
isEnabled: Boolean(annotation.spec.enable),
isHidden: Boolean(annotation.spec.hide),
};
return new DashboardAnnotationsDataLayer(layerState);
});
});
const isDashboardEditable = Boolean(dashboard.editable);
@ -537,15 +514,7 @@ function getGrafanaBuiltInAnnotationDataLayer(dashboard: DashboardV2Spec) {
const grafanaBuiltAnnotation: AnnotationQueryKind = {
kind: 'AnnotationQuery',
spec: {
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'grafana',
datasource: {
name: '-- Grafana --',
},
spec: {},
},
datasource: { uid: '-- Grafana --', type: 'grafana' },
name: 'Annotations & Alerts',
iconColor: DEFAULT_ANNOTATION_COLOR,
enable: true,

@ -57,7 +57,6 @@ import {
validateDashboardSchemaV2,
getDataQueryKind,
getAutoAssignedDSRef,
getVizPanelQueries,
} from './transformSceneToSaveModelSchemaV2';
// Mock dependencies
@ -411,7 +410,9 @@ describe('transformSceneToSaveModelSchemaV2', () => {
expect(result).toMatchSnapshot();
// Check that the annotation layers are correctly transformed
expect(result.annotations).toHaveLength(2);
expect(result.annotations).toHaveLength(3);
// Check annotation layer 3 without initial data source isn't updated with runtime default
expect(result.annotations?.[2].spec.datasource?.type).toBe(undefined);
});
it('should transform the minimum scene to save model schema v2', () => {
@ -833,50 +834,6 @@ describe('getElementDatasource', () => {
});
});
describe('getVizPanelQueries', () => {
it('should handle panel query datasources correctly', () => {
const queryWithDS: SceneDataQuery = {
refId: 'B',
datasource: { uid: 'prometheus-uid', type: 'prometheus' },
};
const queryWithoutDS: SceneDataQuery = {
refId: 'A',
};
// Mock query runner
const queryRunner = new SceneQueryRunner({
queries: [queryWithoutDS, queryWithDS],
datasource: { uid: 'default-ds', type: 'default' },
});
// Create test elements
const vizPanel = new VizPanel({
key: 'panel-1',
pluginId: 'timeseries',
$data: queryRunner,
});
// Mock dsReferencesMapping
const dsReferencesMapping = {
panels: new Map(new Set([['panel-1', new Set<string>(['A'])]])),
variables: new Set<string>(),
annotations: new Set<string>(),
};
const result = getVizPanelQueries(vizPanel, dsReferencesMapping);
expect(result.length).toBe(2);
expect(result[0].spec.query.kind).toBe('DataQuery');
expect(result[0].spec.query.datasource).toBeUndefined(); // ignore datasource if it wasn't provided
expect(result[0].spec.query.group).toBe('default');
expect(result[0].spec.query.version).toBe('v0');
expect(result[1].spec.query.kind).toBe('DataQuery');
expect(result[1].spec.query.datasource?.name).toBe('prometheus-uid');
expect(result[1].spec.query.group).toBe('prometheus');
expect(result[1].spec.query.version).toBe('v0');
});
});
function getMinimalSceneState(body: DashboardLayoutManager): Partial<DashboardSceneState> {
return {
id: 1,
@ -1108,6 +1065,18 @@ function createAnnotationLayers() {
isEnabled: true,
isHidden: true,
}),
// this could happen if a dahboard was created from code and the datasource was not defined
new DashboardAnnotationsDataLayer({
key: 'layer3',
query: {
name: 'query3',
enable: true,
iconColor: 'green',
},
name: 'layer3',
isEnabled: true,
isHidden: true,
}),
];
}

@ -43,7 +43,6 @@ import {
DashboardCursorSync,
FieldConfig,
FieldColor,
defaultDataQueryKind,
} from '../../../../../packages/grafana-schema/src/schema/dashboard/v2alpha1/types.spec.gen';
import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet';
import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene';
@ -251,7 +250,7 @@ function getPanelLinks(panel: VizPanel): DataLink[] {
return [];
}
export function getVizPanelQueries(vizPanel: VizPanel, dsReferencesMapping?: DSReferencesMapping): PanelQueryKind[] {
function getVizPanelQueries(vizPanel: VizPanel, dsReferencesMapping?: DSReferencesMapping): PanelQueryKind[] {
const queries: PanelQueryKind[] = [];
const queryRunner = getQueryRunnerFor(vizPanel);
const vizPanelQueries = queryRunner?.state.queries;
@ -259,22 +258,12 @@ export function getVizPanelQueries(vizPanel: VizPanel, dsReferencesMapping?: DSR
if (vizPanelQueries) {
vizPanelQueries.forEach((query) => {
const queryDatasource = getElementDatasource(vizPanel, query, 'panel', queryRunner, dsReferencesMapping);
const dataQuery: DataQueryKind = {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: getDataQueryKind(query, queryRunner),
datasource: {
name: queryDatasource?.uid,
},
kind: getDataQueryKind(query, queryRunner),
spec: omit(query, 'datasource', 'refId', 'hide'),
};
if (!dataQuery.datasource?.name) {
delete dataQuery.datasource;
}
const querySpec: PanelQuerySpec = {
datasource: queryDatasource,
query: dataQuery,
refId: query.refId,
hidden: Boolean(query.hide),
@ -418,36 +407,18 @@ function getAnnotations(state: DashboardSceneState, dsReferencesMapping?: DSRefe
if (!(layer instanceof dataLayers.AnnotationsDataLayer)) {
continue;
}
const datasource = getElementDatasource(layer, layer.state.query, 'annotation', undefined, dsReferencesMapping);
const layerDs = layer.state.query.datasource;
if (!layerDs) {
throw new Error('Misconfigured AnnotationsDataLayer: Datasource is required for annotations');
}
const result: AnnotationQueryKind = {
kind: 'AnnotationQuery',
spec: {
builtIn: Boolean(layer.state.query.builtIn),
name: layer.state.query.name,
datasource: getElementDatasource(layer, layer.state.query, 'annotation', undefined, dsReferencesMapping),
enable: Boolean(layer.state.isEnabled),
hide: Boolean(layer.state.isHidden),
iconColor: layer.state.query.iconColor,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: layerDs.type!, // Annotation layer has a datasource type provided in runtime.
spec: {},
},
},
};
if (datasource) {
result.spec.query!.datasource = {
name: datasource.uid,
};
}
// Transform v1 dashboard (using target) to v2 structure
// adds extra condition to prioritize query over target
// if query is defined, use it
@ -455,37 +426,24 @@ function getAnnotations(state: DashboardSceneState, dsReferencesMapping?: DSRefe
// Handle built-in annotations
if (layer.state.query.builtIn) {
result.spec.query = {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'grafana', // built-in annotations are always of type grafana
kind: 'grafana', // built-in annotations are always of type grafana
spec: {
...layer.state.query.target,
},
};
} else {
result.spec.query = {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: datasource?.type!,
kind: getAnnotationQueryKind(layer.state.query),
spec: {
...layer.state.query.target,
},
};
if (layer.state.query.datasource?.uid) {
result.spec.query.datasource = {
name: layer.state.query.datasource?.uid,
};
}
}
}
// For annotations without query.query defined (e.g., grafana annotations without tags)
else if (layer.state.query.query?.kind) {
result.spec.query = {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: layer.state.query.query.group || getAnnotationQueryKind(layer.state.query),
datasource: layer.state.query.query.datasource,
kind: layer.state.query.query.kind,
spec: {
...layer.state.query.query.spec,
},
@ -508,15 +466,10 @@ function getAnnotations(state: DashboardSceneState, dsReferencesMapping?: DSRefe
// Store extra properties in the legacyOptions field instead of directly in the spec
if (Object.keys(otherProps).length > 0) {
// // Extract options property and get the rest of the properties
// Extract options property and get the rest of the properties
const { legacyOptions, ...restProps } = otherProps;
if (legacyOptions) {
// Merge options with the rest of the properties
result.spec.legacyOptions = { ...legacyOptions, ...restProps };
}
result.spec.query!.spec = {
...otherProps,
};
// Merge options with the rest of the properties
result.spec.legacyOptions = { ...legacyOptions, ...restProps };
}
// If filter is an empty array, don't save it

@ -5,7 +5,6 @@ import { locationService, reportInteraction } from '@grafana/runtime';
import {
AnnotationQueryKind,
Spec as DashboardV2Spec,
defaultDataQueryKind,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
import { Form } from 'app/core/components/Form/Form';
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
@ -17,8 +16,6 @@ import { ImportDashboardFormV2 } from './ImportDashboardFormV2';
const IMPORT_FINISHED_EVENT_NAME = 'dashboard_import_imported';
type FormData = SaveDashboardCommand<DashboardV2Spec> & { [key: `datasource-${string}`]: string };
export function ImportDashboardOverviewV2() {
const [uidReset, setUidReset] = useState(false);
const dispatch = useDispatch();
@ -37,29 +34,24 @@ export function ImportDashboardOverviewV2() {
dispatch(clearLoadedDashboard());
}
async function onSubmit(form: FormData) {
async function onSubmit(form: SaveDashboardCommand<DashboardV2Spec>) {
reportInteraction(IMPORT_FINISHED_EVENT_NAME);
const dashboardWithDataSources: DashboardV2Spec = {
...dashboard,
title: form.dashboard.title,
annotations: dashboard.annotations?.map((annotation: AnnotationQueryKind) => {
const dsType = annotation.spec.query?.spec.group;
if (dsType) {
if (annotation.spec.query?.kind) {
const dsType = annotation.spec.query.kind;
if (form[`datasource-${dsType}` as keyof typeof form]) {
const ds = form[`datasource-${dsType}` as keyof typeof form] as { uid: string; type: string };
return {
...annotation,
spec: {
...annotation.spec,
query: {
kind: 'DataQuery',
group: dsType,
version: defaultDataQueryKind().version,
datasource: { name: ds.uid },
spec: {
...annotation.spec.query?.spec,
},
datasource: {
uid: ds.uid,
type: ds.type,
},
},
};
@ -69,23 +61,18 @@ export function ImportDashboardOverviewV2() {
}),
variables: dashboard.variables?.map((variable) => {
if (variable.kind === 'QueryVariable') {
const dsType = variable.spec.query?.spec.group;
if (dsType) {
if (variable.spec.query?.kind) {
const dsType = variable.spec.query.kind;
if (form[`datasource-${dsType}` as keyof typeof form]) {
const ds = form[`datasource-${dsType}` as keyof typeof form] as { uid: string; type: string };
return {
...variable,
spec: {
...variable.spec,
query: {
...variable.spec.query,
spec: {
...variable.spec.query.spec,
group: ds.type,
datasource: {
name: ds.uid,
},
},
datasource: {
...variable.spec.datasource,
uid: ds.uid,
type: ds.type,
},
options: [],
current: {
@ -170,7 +157,7 @@ export function ImportDashboardOverviewV2() {
return (
<>
<Form<FormData>
<Form<SaveDashboardCommand<DashboardV2Spec> & { [key: `datasource-${string}`]: string }>
onSubmit={onSubmit}
defaultValues={{ dashboard, k8s: { annotations: { 'grafana.app/folder': folder.uid } } }}
validateOnMount

@ -52,8 +52,7 @@ export function validateVariable<
expect(sceneVariable?.state.pluginId).toBe(variableKind.spec.pluginId);
}
if (sceneVariable instanceof QueryVariable && variableKind.kind === 'QueryVariable') {
expect(sceneVariable?.state.datasource?.type).toBe(variableKind.spec.query?.group);
expect(sceneVariable?.state.datasource?.uid).toBe(variableKind.spec.query?.datasource?.name);
expect(sceneVariable?.state.datasource).toBe(variableKind.spec.datasource);
expect(sceneVariable?.state.query).toEqual(variableKind.spec.query.spec);
}
if (sceneVariable instanceof CustomVariable && variableKind.kind === 'CustomVariable') {

@ -2,7 +2,6 @@ import { AnnotationQuery, DataQuery, VariableModel, VariableRefresh, Panel } fro
import { handyTestingSchema } from '@grafana/schema/dist/esm/schema/dashboard/v2_examples';
import {
Spec as DashboardV2Spec,
defaultDataQueryKind,
GridLayoutItemKind,
GridLayoutKind,
PanelKind,
@ -467,14 +466,10 @@ describe('ResponseTransformers', () => {
{
kind: 'PanelQuery',
spec: {
datasource: 'datasource1',
hidden: false,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
datasource: {
name: 'datasource1',
},
kind: 'prometheus',
spec: {
expr: 'test-query',
},
@ -978,9 +973,11 @@ describe('ResponseTransformers', () => {
result.forEach((query) => {
expect(query.kind).toBe('PanelQuery');
expect(query.spec.query.group).toEqual('theoretical-ds');
expect(query.spec.query.datasource?.name).toEqual('theoretical-uid');
expect(query.spec.query.kind).toBe('DataQuery');
expect(query.spec.datasource).toEqual({
type: 'theoretical-ds',
uid: 'theoretical-uid',
});
expect(query.spec.query.kind).toBe('theoretical-ds');
});
});
@ -1006,9 +1003,11 @@ describe('ResponseTransformers', () => {
result.forEach((query) => {
expect(query.kind).toBe('PanelQuery');
expect(query.spec.query.group).toEqual('theoretical-ds');
expect(query.spec.query.datasource?.name).toEqual('theoretical-uid');
expect(query.spec.query.kind).toBe('DataQuery');
expect(query.spec.datasource).toEqual({
type: 'theoretical-ds',
uid: 'theoretical-uid',
});
expect(query.spec.query.kind).toBe('theoretical-ds');
});
});
});
@ -1018,8 +1017,7 @@ describe('ResponseTransformers', () => {
const { spec: v2Spec } = v2;
expect(v1.name).toBe(v2Spec.name);
expect(v1.datasource?.type).toBe(v2Spec.query?.spec.group);
expect(v1.datasource?.uid).toBe(v2Spec.query?.spec.datasource?.name);
expect(v1.datasource).toBe(v2Spec.datasource);
expect(v1.enable).toBe(v2Spec.enable);
expect(v1.hide).toBe(v2Spec.hide);
expect(v1.iconColor).toBe(v2Spec.iconColor);
@ -1045,10 +1043,7 @@ describe('ResponseTransformers', () => {
return {
refId: q.spec.refId,
hide: q.spec.hidden,
datasource: {
type: q.spec.query.spec.group,
uid: q.spec.query.spec.datasource?.uid,
},
datasource: q.spec.datasource,
...q.spec.query.spec,
};
})
@ -1098,24 +1093,18 @@ describe('ResponseTransformers', () => {
};
expect(v2Common).toEqual(v1Common);
if (v2.kind === 'QueryVariable') {
expect(v2.spec.query).toMatchObject({
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: (v1.datasource?.type || getDefaultDataSourceRef()?.type) ?? 'grafana',
...(v1.datasource?.uid && {
datasource: {
name: v1.datasource?.uid,
},
}),
});
expect(v2.spec.datasource).toEqual(v1.datasource);
if (typeof v1.query === 'string') {
expect(v2.spec.query.spec).toEqual({
[LEGACY_STRING_VALUE_KEY]: v1.query,
});
expect(v2.spec.query.spec[LEGACY_STRING_VALUE_KEY]).toEqual(v1.query);
} else {
expect(v2.spec.query.spec).toEqual({
...(typeof v1.query === 'object' ? v1.query : {}),
expect(v2.spec.query).toEqual({
kind: v1.datasource?.type,
spec: {
...(typeof v1.query === 'object' ? v1.query : {}),
},
});
}
}

@ -38,7 +38,6 @@ import {
LibraryPanelKind,
PanelKind,
GridLayoutItemKind,
defaultDataQueryKind,
RowsLayoutRowKind,
GridLayoutKind,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
@ -503,13 +502,9 @@ export function getPanelQueries(targets: DataQuery[], panelDatasource: DataSourc
spec: {
refId: t.refId,
hidden: t.hide ?? false,
datasource: t.datasource ? t.datasource : panelDatasource,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: t.datasource?.type || panelDatasource.type!,
datasource: {
name: t.datasource?.uid || panelDatasource.uid!,
},
kind: t.datasource?.type || panelDatasource.type!,
spec: {
...query,
},
@ -573,12 +568,7 @@ function getVariables(vars: TypedVariableModel[]): DashboardV2Spec['variables']
regex: v.regex || '',
sort: transformSortVariableToEnum(v.sort),
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: v.datasource?.type ?? getDefaultDatasourceType(),
datasource: {
name: v.datasource?.uid,
},
kind: v.datasource?.type || getDefaultDatasourceType(),
spec: query,
},
allowCustomValue: v.allowCustomValue ?? true,
@ -732,12 +722,7 @@ function getAnnotations(annotations: AnnotationQuery[]): DashboardV2Spec['annota
iconColor: a.iconColor,
builtIn: Boolean(a.builtIn),
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: a.datasource?.type || getDefaultDatasourceType(),
datasource: {
name: a.datasource?.uid,
},
kind: a.datasource?.type || getDefaultDatasourceType(),
spec: {
...a.target,
},
@ -772,10 +757,7 @@ function getVariablesV1(vars: DashboardV2Spec['variables']): VariableModel[] {
LEGACY_STRING_VALUE_KEY in v.spec.query.spec
? v.spec.query.spec[LEGACY_STRING_VALUE_KEY]
: v.spec.query.spec,
datasource: {
type: v.spec.query?.spec.group,
uid: v.spec.query?.spec.datasource?.name,
},
datasource: v.spec.datasource,
sort: transformSortVariableToEnumV1(v.spec.sort),
refresh: transformVariableRefreshToEnumV1(v.spec.refresh),
regex: v.spec.regex,
@ -897,10 +879,7 @@ function getAnnotationsV1(annotations: DashboardV2Spec['annotations']): Annotati
return annotations.map((a) => {
return {
name: a.spec.name,
datasource: {
type: a.spec.query?.spec.group,
uid: a.spec.query?.spec.datasource?.name,
},
datasource: a.spec.datasource,
enable: a.spec.enable,
hide: a.spec.hide,
iconColor: a.spec.iconColor,
@ -971,10 +950,7 @@ function transformV2PanelToV1Panel(
return {
refId: q.spec.refId,
hide: q.spec.hidden,
datasource: {
uid: q.spec.query.spec.datasource?.uid,
type: q.spec.query.spec.group,
},
datasource: q.spec.datasource,
...q.spec.query.spec,
};
}),

@ -7,7 +7,6 @@ import {
defaultSpec as defaultDashboardV2Spec,
defaultPanelSpec,
defaultQueryVariableSpec,
defaultDataQueryKind,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
import { browseDashboardsAPI } from 'app/features/browse-dashboards/api/browseDashboardsAPI';
import { getLibraryPanel } from 'app/features/library-panels/state/api';
@ -837,9 +836,7 @@ describe('processV2Datasources', () => {
refId: 'A',
hidden: false,
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'prometheus',
kind: 'prometheus',
spec: {
expr: 'access_evaluation_duration_count',
range: true,
@ -865,9 +862,7 @@ describe('processV2Datasources', () => {
...defaultQueryVariableSpec(),
name: 'var1',
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'loki',
kind: 'loki',
spec: {
expr: 'access_evaluation_duration_count',
range: true,
@ -885,9 +880,7 @@ describe('processV2Datasources', () => {
hide: false,
iconColor: 'red',
query: {
kind: 'DataQuery',
version: defaultDataQueryKind().version,
group: 'loki',
kind: 'loki',
spec: {
expr: 'access_evaluation_duration_count',
range: true,
@ -989,17 +982,13 @@ describe('processV2DatasourceInput', () => {
...defaultQueryVariableSpec(),
name: 'var2WithGrafanaDs',
query: {
kind: 'DataQuery' as const,
version: defaultDataQueryKind().version,
group: 'grafana',
kind: 'grafana',
spec: {
expr: 'access_evaluation_duration_count',
range: true,
panelId: 2,
},
},
},
};
const result = await processV2DatasourceInput(queryVariable.spec, {});
expect(result).toEqual({});
});
@ -1012,9 +1001,7 @@ describe('processV2DatasourceInput', () => {
refId: 'A',
hidden: false,
query: {
kind: 'DataQuery' as const,
version: defaultDataQueryKind().version,
group: 'datasource',
kind: 'datasource',
spec: {
panelId: 2,
},

@ -168,7 +168,6 @@ export function processV2Datasources(dashboard: DashboardV2Spec): ThunkResult<vo
if (element.kind !== 'Panel') {
throw new Error('Only panels are currenlty supported in v2 dashboards');
}
if (element.spec.data.spec.queries.length > 0) {
for (const query of element.spec.data.spec.queries) {
inputs = await processV2DatasourceInput(query.spec, inputs);
@ -333,43 +332,43 @@ export function getFolderByUid(uid: string): Promise<{ uid: string; title: strin
}
export async function processV2DatasourceInput(
spec: PanelQueryKind['spec'] | QueryVariableKind['spec'] | AnnotationQueryKind['spec'],
obj: PanelQueryKind['spec'] | QueryVariableKind['spec'] | AnnotationQueryKind['spec'],
inputs: Record<string, DataSourceInput> = {}
) {
const datasourceRef = spec.query.datasource;
let dataSourceInput: DataSourceInput | undefined;
const dsType = spec.query.group;
if (!datasourceRef) {
const datasourceRef = obj?.datasource;
if (!datasourceRef && obj?.query) {
const dsType = obj.query.kind;
// if dsType is grafana, it means we are using a built-in annotation or default grafana datasource, in those
// cases we don't need to map it
// "datasource" type is what we call "--Dashboard--" datasource <.-.>
if (dsType === 'grafana' || dsType === 'datasource') {
return inputs;
}
}
const datasource = await getDatasourceSrv().get({ type: dsType });
if (datasource) {
dataSourceInput = {
name: datasource.name,
label: datasource.name,
info: `Select a ${datasource.name} data source`,
value: datasource.uid,
type: InputType.DataSource,
pluginId: datasource.meta?.id,
};
inputs[datasource.meta?.id] = dataSourceInput;
} else {
dataSourceInput = {
name: dsType,
label: dsType,
info: `No data sources of type ${dsType} found`,
value: '',
type: InputType.DataSource,
pluginId: dsType,
};
inputs[dsType] = dataSourceInput;
const datasource = await getDatasourceSrv().get({ type: dsType });
let dataSourceInput: DataSourceInput | undefined;
if (datasource) {
dataSourceInput = {
name: datasource.name,
label: datasource.name,
info: `Select a ${datasource.name} data source`,
value: datasource.uid,
type: InputType.DataSource,
pluginId: datasource.meta?.id,
};
inputs[datasource.meta?.id] = dataSourceInput;
} else {
dataSourceInput = {
name: dsType,
label: dsType,
info: `No data sources of type ${dsType} found`,
value: '',
type: InputType.DataSource,
pluginId: dsType,
};
inputs[dsType] = dataSourceInput;
}
}
return inputs;
}

Loading…
Cancel
Save