Merge remote-tracking branch 'origin/main' into ds-apiserver-with-configs
This commit is contained in:
@@ -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,14 +385,15 @@ VizConfigKind: {
|
||||
}
|
||||
|
||||
AnnotationQuerySpec: {
|
||||
query: DataQueryKind
|
||||
datasource?: DataSourceRef
|
||||
query?: DataQueryKind
|
||||
enable: bool
|
||||
hide: bool
|
||||
iconColor: string
|
||||
name: string
|
||||
builtIn?: bool | *false
|
||||
filter?: AnnotationPanelFilter
|
||||
legacyOptions?: [string]: _ // Catch-all field for datasource-specific properties. Should not be available in as code tooling.
|
||||
legacyOptions?: [string]: _ //Catch-all field for datasource-specific properties
|
||||
}
|
||||
|
||||
AnnotationQueryKind: {
|
||||
@@ -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,14 +389,15 @@ VizConfigKind: {
|
||||
}
|
||||
|
||||
AnnotationQuerySpec: {
|
||||
query: DataQueryKind
|
||||
datasource?: DataSourceRef
|
||||
query?: DataQueryKind
|
||||
enable: bool
|
||||
hide: bool
|
||||
iconColor: string
|
||||
name: string
|
||||
builtIn?: bool | *false
|
||||
filter?: AnnotationPanelFilter
|
||||
legacyOptions?: [string]: _ // Catch-all field for datasource-specific properties. Should not be available in as code tooling.
|
||||
legacyOptions?: [string]: _ //Catch-all field for datasource-specific properties
|
||||
}
|
||||
|
||||
AnnotationQueryKind: {
|
||||
@@ -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,42 +23,49 @@ 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"`
|
||||
// Catch-all field for datasource-specific properties. Should not be available in as code tooling.
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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": {
|
||||
@@ -643,7 +646,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardAnnotationQuerySpec(ref common.
|
||||
},
|
||||
"legacyOptions": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Catch-all field for datasource-specific properties. Should not be available in as code tooling.",
|
||||
Description: "Catch-all field for datasource-specific properties",
|
||||
Type: []string{"object"},
|
||||
AdditionalProperties: &spec.SchemaOrBool{
|
||||
Allows: true,
|
||||
@@ -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",
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
{
|
||||
"kind": "Dashboard",
|
||||
"apiVersion": "dashboard.grafana.app/v1beta1",
|
||||
"metadata": {
|
||||
"name": "test-v1-annotations",
|
||||
"annotations": {
|
||||
"hello": "world"
|
||||
},
|
||||
"labels": {
|
||||
"region": "west"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": false,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"type": "dashboard"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": false,
|
||||
"iconColor": "blue",
|
||||
"name": "testdata-annos",
|
||||
"target": {
|
||||
"lines": 10,
|
||||
"refId": "Anno",
|
||||
"scenarioId": "annotations"
|
||||
}
|
||||
},
|
||||
{
|
||||
"enable": true,
|
||||
"hide": false,
|
||||
"iconColor": "blue",
|
||||
"name": "no-ds-testdata-annos",
|
||||
"target": {
|
||||
"lines": 10,
|
||||
"refId": "Anno",
|
||||
"scenarioId": "annotations"
|
||||
}
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "prometheus",
|
||||
"uid": "gdev-prometheus"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": false,
|
||||
"iconColor": "yellow",
|
||||
"name": "prom-annos",
|
||||
"target": {
|
||||
"expr": "{action=\"add_client\"}",
|
||||
"interval": "",
|
||||
"lines": 10,
|
||||
"refId": "Anno",
|
||||
"scenarioId": "annotations"
|
||||
}
|
||||
},
|
||||
{
|
||||
"enable": true,
|
||||
"hide": false,
|
||||
"iconColor": "yellow",
|
||||
"name": "no-ds-prom-annos",
|
||||
"target": {
|
||||
"expr": "{action=\"add_client\"}",
|
||||
"interval": "",
|
||||
"lines": 10,
|
||||
"refId": "Anno",
|
||||
"scenarioId": "annotations"
|
||||
}
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "grafana-postgresql-datasource",
|
||||
"uid": "PBBCEC2D313BC06C3"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": false,
|
||||
"iconColor": "red",
|
||||
"name": "postgress-annos",
|
||||
"target": {
|
||||
"editorMode": "builder",
|
||||
"format": "table",
|
||||
"lines": 10,
|
||||
"rawSql": "",
|
||||
"refId": "Anno",
|
||||
"scenarioId": "annotations",
|
||||
"sql": {
|
||||
"columns": [
|
||||
{
|
||||
"parameters": [],
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"groupBy": [
|
||||
{
|
||||
"property": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "groupBy"
|
||||
}
|
||||
],
|
||||
"limit": 50
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "elasticsearch",
|
||||
"uid": "gdev-elasticsearch"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": false,
|
||||
"iconColor": "red",
|
||||
"name": "elastic - annos",
|
||||
"tagsField": "asd",
|
||||
"target": {
|
||||
"lines": 10,
|
||||
"query": "test query",
|
||||
"refId": "Anno",
|
||||
"scenarioId": "annotations"
|
||||
},
|
||||
"textField": "asd",
|
||||
"timeEndField": "asdas",
|
||||
"timeField": "asd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"hideZeros": false,
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "12.1.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "New panel",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"preload": false,
|
||||
"schemaVersion": 41,
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "browser",
|
||||
"title": "Test: V1 dashboard with annotations",
|
||||
"version": 8
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -97,7 +97,7 @@ require (
|
||||
github.com/grafana/grafana-api-golang-client v0.27.0 // @grafana/alerting-backend
|
||||
github.com/grafana/grafana-app-sdk v0.39.2 // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/grafana-app-sdk/logging v0.39.1 // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/grafana-aws-sdk v1.0.3 // @grafana/aws-datasources
|
||||
github.com/grafana/grafana-aws-sdk v1.0.4 // @grafana/aws-datasources
|
||||
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // @grafana/partner-datasources
|
||||
github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 // @grafana/grafana-operator-experience-squad
|
||||
github.com/grafana/grafana-google-sdk-go v0.4.1 // @grafana/partner-datasources
|
||||
|
||||
@@ -1595,8 +1595,8 @@ github.com/grafana/grafana-app-sdk v0.39.2 h1:ymfr+1318t+JC9U2OYrzVpGmNG/aJONUmF
|
||||
github.com/grafana/grafana-app-sdk v0.39.2/go.mod h1:t0m6q561lpoHQCixS9LUHFUhUzDClzNtm7BH60gHVSY=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.39.1 h1:lI5rbrheuwVPuyIM6LIuEYOCSpgmXahfKtqeMyhbGPU=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.39.1/go.mod h1:WhDENSnaGHtyVVwZGVnAR7YLvh2xlLDYR3D7E6h7XVk=
|
||||
github.com/grafana/grafana-aws-sdk v1.0.3 h1:FtGiTrdlPXuQmLqeZdyNjjkNKOOdvQ4sxWw8MtJc6ow=
|
||||
github.com/grafana/grafana-aws-sdk v1.0.3/go.mod h1:hO7q7yWV+t6dmiyJjMa3IbuYnYkBua+G/IAlOPVIYKE=
|
||||
github.com/grafana/grafana-aws-sdk v1.0.4 h1:D14UAehsOqpjliHmHzveRQ1p43KCsMzdmb7GovWj+SY=
|
||||
github.com/grafana/grafana-aws-sdk v1.0.4/go.mod h1:hO7q7yWV+t6dmiyJjMa3IbuYnYkBua+G/IAlOPVIYKE=
|
||||
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg=
|
||||
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo=
|
||||
github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 h1:S4kHwr//AqhtL9xHBtz1gqVgZQeCRGTxjgsRBAkpjKY=
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -355,6 +355,7 @@
|
||||
"leven": "^4.0.0",
|
||||
"lodash": "4.17.21",
|
||||
"logfmt": "^1.3.2",
|
||||
"lossless-json": "^4.1.1",
|
||||
"lru-cache": "11.1.0",
|
||||
"lru-memoize": "^1.1.0",
|
||||
"lucene": "^2.1.1",
|
||||
|
||||
@@ -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,19 +11,19 @@ export const defaultAnnotationQueryKind = (): AnnotationQueryKind => ({
|
||||
});
|
||||
|
||||
export interface AnnotationQuerySpec {
|
||||
query: DataQueryKind;
|
||||
datasource?: DataSourceRef;
|
||||
query?: DataQueryKind;
|
||||
enable: boolean;
|
||||
hide: boolean;
|
||||
iconColor: string;
|
||||
name: string;
|
||||
builtIn?: boolean;
|
||||
filter?: AnnotationPanelFilter;
|
||||
// Catch-all field for datasource-specific properties. Should not be available in as code tooling.
|
||||
// Catch-all field for datasource-specific properties
|
||||
legacyOptions?: Record<string, any>;
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
@@ -57,7 +57,7 @@ func convertToK8sResource(v *folder.Folder, namespacer request.NamespaceMapper)
|
||||
},
|
||||
Spec: folders.FolderSpec{
|
||||
Title: v.Title,
|
||||
Description: descr(v.Description),
|
||||
Description: &v.Description,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -94,10 +94,3 @@ func convertToK8sResource(v *folder.Folder, namespacer request.NamespaceMapper)
|
||||
f.UID = gapiutil.CalculateClusterWideUID(f)
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func descr(str string) *string {
|
||||
if str == "" {
|
||||
return nil
|
||||
}
|
||||
return &str
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
if !db.IsTestDbSQLite() {
|
||||
t.Skip("test only on sqlite for now")
|
||||
}
|
||||
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
EnableFeatureToggles: []string{
|
||||
@@ -120,71 +124,94 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
||||
}`, string(v1Disco))
|
||||
})
|
||||
|
||||
t.Run("with dual write (unified storage, mode 0)", func(t *testing.T) {
|
||||
doFolderTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: grafanarest.Mode0,
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
},
|
||||
}))
|
||||
})
|
||||
// test on all dualwriter modes
|
||||
for mode := 0; mode <= 4; mode++ {
|
||||
modeDw := grafanarest.DualWriterMode(mode)
|
||||
|
||||
t.Run("with dual write (unified storage, mode 1)", func(t *testing.T) {
|
||||
doFolderTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: grafanarest.Mode1,
|
||||
t.Run(fmt.Sprintf("with dual write (unified storage, mode %v)", modeDw), func(t *testing.T) {
|
||||
doFolderTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
},
|
||||
}))
|
||||
})
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
t.Run("with dual write (unified storage, mode 1, create nested folders)", func(t *testing.T) {
|
||||
doNestedCreateTest(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: grafanarest.Mode1,
|
||||
t.Run(fmt.Sprintf("with dual write (unified storage, mode %v, create nested folders)", modeDw), func(t *testing.T) {
|
||||
doNestedCreateTest(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
})
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
t.Run("with dual write (unified storage, mode 1, create existing folder)", func(t *testing.T) {
|
||||
doCreateDuplicateFolderTest(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: grafanarest.Mode1,
|
||||
t.Run(fmt.Sprintf("with dual write (unified storage, mode %v, create existing folder)", modeDw), func(t *testing.T) {
|
||||
doCreateDuplicateFolderTest(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
})
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("when creating a folder, mode %v, it should trim leading and trailing spaces", modeDw), func(t *testing.T) {
|
||||
doCreateEnsureTitleIsTrimmedTest(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
t.Run(fmt.Sprintf("with dual write (unified storage, mode %v, create circular reference folder)", modeDw), func(t *testing.T) {
|
||||
doCreateCircularReferenceFolderTest(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: modeDw,
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
// This is a general test for the unified storage list operation. We don't have a common test
|
||||
// directory for now, so we (search and storage) keep it here as we own this part of the tests.
|
||||
@@ -217,40 +244,6 @@ func TestIntegrationFoldersApp(t *testing.T) {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("when creating a folder it should trim leading and trailing spaces", func(t *testing.T) {
|
||||
doCreateEnsureTitleIsTrimmedTest(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: grafanarest.Mode1,
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
t.Run("with dual write (unified storage, mode 1, create circular reference folder)", func(t *testing.T) {
|
||||
doCreateCircularReferenceFolderTest(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
folders.RESOURCEGROUP: {
|
||||
DualWriterMode: grafanarest.Mode1,
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagKubernetesClientDashboardsFolders,
|
||||
featuremgmt.FlagNestedFolders,
|
||||
},
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func doFolderTests(t *testing.T, helper *apis.K8sTestHelper) *apis.K8sTestHelper {
|
||||
@@ -276,20 +269,25 @@ func doFolderTests(t *testing.T, helper *apis.K8sTestHelper) *apis.K8sTestHelper
|
||||
require.NotNil(t, legacyCreate.Result)
|
||||
uid := legacyCreate.Result.UID
|
||||
require.NotEmpty(t, uid)
|
||||
//nolint:staticcheck
|
||||
id := legacyCreate.Result.ID
|
||||
require.NotEmpty(t, id)
|
||||
idStr := fmt.Sprintf("%d", id)
|
||||
|
||||
expectedResult := `{
|
||||
"apiVersion": "folder.grafana.app/v1beta1",
|
||||
"kind": "Folder",
|
||||
"metadata": {
|
||||
"creationTimestamp": "${creationTimestamp}",
|
||||
"labels": {"grafana.app/deprecatedInternalID":"1"},
|
||||
"labels": {"grafana.app/deprecatedInternalID":"` + idStr + `"},
|
||||
"name": "` + uid + `",
|
||||
"namespace": "default",
|
||||
"resourceVersion": "${resourceVersion}",
|
||||
"uid": "${uid}"
|
||||
},
|
||||
"spec": {
|
||||
"title": "Test"
|
||||
"title": "Test",
|
||||
"description": ""
|
||||
},
|
||||
"status": {}
|
||||
}`
|
||||
@@ -592,6 +590,9 @@ func TestIntegrationFolderCreatePermissions(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
if !db.IsTestDbSQLite() {
|
||||
t.Skip("test only on sqlite for now")
|
||||
}
|
||||
|
||||
folderWithoutParentInput := "{ \"uid\": \"uid\", \"title\": \"Folder\"}"
|
||||
folderWithParentInput := "{ \"uid\": \"uid\", \"title\": \"Folder\", \"parentUid\": \"parentuid\"}"
|
||||
@@ -716,6 +717,9 @@ func TestIntegrationFolderGetPermissions(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
if !db.IsTestDbSQLite() {
|
||||
t.Skip("test only on sqlite for now")
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
description string
|
||||
@@ -866,6 +870,9 @@ func TestIntegrationFoldersCreateAPIEndpointK8S(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
if !db.IsTestDbSQLite() {
|
||||
t.Skip("test only on sqlite for now")
|
||||
}
|
||||
|
||||
folderWithoutParentInput := "{ \"uid\": \"uid\", \"title\": \"Folder\"}"
|
||||
folderWithTitleEmpty := "{ \"title\": \"\"}"
|
||||
@@ -1027,7 +1034,6 @@ func TestIntegrationFoldersGetAPIEndpointK8S(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
if !db.IsTestDbSQLite() {
|
||||
t.Skip("test only on sqlite for now")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -1276,7 +1278,7 @@
|
||||
"default": ""
|
||||
},
|
||||
"legacyOptions": {
|
||||
"description": "Catch-all field for datasource-specific properties. Should not be available in as code tooling.",
|
||||
"description": "Catch-all field for datasource-specific properties",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object"
|
||||
@@ -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": [
|
||||
|
||||
@@ -15,6 +15,17 @@ jest.mock('@grafana/runtime', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
Object.assign(navigator, {
|
||||
clipboard: {
|
||||
write: jest.fn().mockResolvedValue(undefined),
|
||||
writeText: jest.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
});
|
||||
|
||||
document.execCommand = jest.fn();
|
||||
});
|
||||
|
||||
describe('createShortLink', () => {
|
||||
it('creates short link', async () => {
|
||||
const shortUrl = await createShortLink('www.verylonglinkwehavehere.com');
|
||||
@@ -23,11 +34,34 @@ describe('createShortLink', () => {
|
||||
});
|
||||
|
||||
describe('createAndCopyShortLink', () => {
|
||||
it('copies short link to clipboard', async () => {
|
||||
it('copies short link to clipboard via document.execCommand when navigator.clipboard is undefined', async () => {
|
||||
Object.assign(navigator, {
|
||||
clipboard: {
|
||||
write: undefined,
|
||||
},
|
||||
});
|
||||
document.execCommand = jest.fn();
|
||||
await createAndCopyShortLink('www.verylonglinkwehavehere.com');
|
||||
expect(document.execCommand).toHaveBeenCalledWith('copy');
|
||||
});
|
||||
|
||||
it('copies short link to clipboard via navigator.clipboard.writeText when ClipboardItem is undefined', async () => {
|
||||
window.isSecureContext = true;
|
||||
await createAndCopyShortLink('www.verylonglinkwehavehere.com');
|
||||
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('www.short.com');
|
||||
});
|
||||
|
||||
it('copies short link to clipboard via navigator.clipboard.write and ClipboardItem when it is defined', async () => {
|
||||
global.ClipboardItem = jest.fn().mockImplementation(() => ({
|
||||
type: 'text/plain',
|
||||
size: 0,
|
||||
slice: jest.fn(),
|
||||
supports: jest.fn().mockReturnValue(true),
|
||||
// eslint-disable-next-line
|
||||
})) as any;
|
||||
await createAndCopyShortLink('www.verylonglinkwehavehere.com');
|
||||
expect(navigator.clipboard.write).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLogsPermalinkRange', () => {
|
||||
|
||||
@@ -35,13 +35,30 @@ export const createShortLink = memoizeOne(async function (path: string) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates a ClipboardItem for the shortened link. This is used due to clipboard issues in Safari after making async calls.
|
||||
* See https://github.com/grafana/grafana/issues/106889
|
||||
* @param path - The long path to share.
|
||||
* @returns A ClipboardItem for the shortened link.
|
||||
*/
|
||||
const createShortLinkClipboardItem = (path: string) => {
|
||||
return new ClipboardItem({
|
||||
'text/plain': createShortLink(path),
|
||||
});
|
||||
};
|
||||
|
||||
export const createAndCopyShortLink = async (path: string) => {
|
||||
const shortLink = await createShortLink(path);
|
||||
if (shortLink) {
|
||||
copyStringToClipboard(shortLink);
|
||||
if (typeof ClipboardItem !== 'undefined' && navigator.clipboard.write) {
|
||||
navigator.clipboard.write([createShortLinkClipboardItem(path)]);
|
||||
dispatch(notifyApp(createSuccessNotification('Shortened link copied to clipboard')));
|
||||
} else {
|
||||
dispatch(notifyApp(createErrorNotification('Error generating shortened link')));
|
||||
const shortLink = await createShortLink(path);
|
||||
if (shortLink) {
|
||||
copyStringToClipboard(shortLink);
|
||||
dispatch(notifyApp(createSuccessNotification('Shortened link copied to clipboard')));
|
||||
} else {
|
||||
dispatch(notifyApp(createErrorNotification('Error generating shortened link')));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ const newShareButtonSelector = e2eSelectors.pages.Dashboard.DashNav.newShareButt
|
||||
export const ShareDashboardButton = ({ dashboard }: ToolbarActionProps) => {
|
||||
const [_, buildUrl] = useAsyncFn(async () => {
|
||||
DashboardInteractions.toolbarShareClick();
|
||||
return await buildShareUrl(dashboard);
|
||||
await buildShareUrl(dashboard);
|
||||
}, [dashboard]);
|
||||
|
||||
return (
|
||||
|
||||
+8
-245
@@ -10,13 +10,10 @@ import { Dashboard, VariableModel } from '@grafana/schema';
|
||||
import {
|
||||
Spec as DashboardV2Spec,
|
||||
defaultSpec as defaultDashboardV2Spec,
|
||||
defaultDataQueryKind,
|
||||
defaultPanelSpec,
|
||||
defaultTimeSettingsSpec,
|
||||
GridLayoutKind,
|
||||
PanelKind,
|
||||
PanelSpec,
|
||||
QueryVariableKind,
|
||||
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
|
||||
import { DEFAULT_ANNOTATION_COLOR } from '@grafana/ui';
|
||||
import { AnnoKeyDashboardSnapshotOriginalUrl } from 'app/features/apiserver/types';
|
||||
@@ -50,13 +47,11 @@ jest.mock('@grafana/runtime', () => ({
|
||||
name: 'Grafana',
|
||||
meta: { id: 'grafana' },
|
||||
type: 'datasource',
|
||||
uid: 'grafana',
|
||||
},
|
||||
prometheus: {
|
||||
name: 'prometheus',
|
||||
meta: { id: 'prometheus' },
|
||||
type: 'datasource',
|
||||
uid: 'prometheus-uid',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -728,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,
|
||||
@@ -894,226 +884,6 @@ describe('DashboardSceneSerializer', () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe('data source references persistence', () => {
|
||||
it('should not fill data source references for annotations when input did not contain it', () => {
|
||||
const dashboard = setupV2({
|
||||
annotations: [
|
||||
{
|
||||
kind: 'AnnotationQuery',
|
||||
spec: {
|
||||
builtIn: false,
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'blue',
|
||||
name: 'prom-annotations',
|
||||
query: {
|
||||
group: 'prometheus',
|
||||
kind: 'DataQuery',
|
||||
spec: {
|
||||
refId: 'Anno',
|
||||
},
|
||||
version: 'v0',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const saveAsModel = serializer.getSaveAsModel(dashboard, baseOptions);
|
||||
// referencing index 1 as transformation adds built in annotation query
|
||||
expect(saveAsModel.annotations[1].spec.query.datasource).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fill data source references for annotations when input did contain it', () => {
|
||||
const dashboard = setupV2({
|
||||
annotations: [
|
||||
{
|
||||
kind: 'AnnotationQuery',
|
||||
spec: {
|
||||
builtIn: false,
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'blue',
|
||||
name: 'prom-annotations',
|
||||
query: {
|
||||
group: 'prometheus',
|
||||
kind: 'DataQuery',
|
||||
datasource: {
|
||||
name: 'prometheus-uid',
|
||||
},
|
||||
spec: {
|
||||
refId: 'Anno',
|
||||
},
|
||||
version: 'v0',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const saveAsModel = serializer.getSaveAsModel(dashboard, baseOptions);
|
||||
// referencing index 1 as transformation adds built in annotation query
|
||||
expect(saveAsModel.annotations[1].spec.query.datasource).toEqual({
|
||||
name: 'prometheus-uid',
|
||||
});
|
||||
});
|
||||
it('should not fill data source references for panel queries when input did not contain it', () => {
|
||||
const dashboard = setupV2({
|
||||
elements: {
|
||||
'panel-1': {
|
||||
kind: 'Panel',
|
||||
spec: {
|
||||
...defaultPanelSpec(),
|
||||
data: {
|
||||
kind: 'QueryGroup',
|
||||
spec: {
|
||||
transformations: [],
|
||||
queryOptions: {},
|
||||
queries: [
|
||||
{
|
||||
kind: 'PanelQuery',
|
||||
spec: {
|
||||
refId: 'A',
|
||||
hidden: false,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
group: 'prometheus',
|
||||
version: 'v0',
|
||||
spec: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const saveAsModel = serializer.getSaveAsModel(dashboard, baseOptions);
|
||||
expect(
|
||||
(saveAsModel.elements['panel-1'] as PanelKind).spec.data.spec.queries[0].spec.query.datasource
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fill data source references for panel queries when input did contain it', () => {
|
||||
const dashboard = setupV2({
|
||||
elements: {
|
||||
'panel-1': {
|
||||
kind: 'Panel',
|
||||
spec: {
|
||||
...defaultPanelSpec(),
|
||||
data: {
|
||||
kind: 'QueryGroup',
|
||||
spec: {
|
||||
transformations: [],
|
||||
queryOptions: {},
|
||||
queries: [
|
||||
{
|
||||
kind: 'PanelQuery',
|
||||
spec: {
|
||||
refId: 'A',
|
||||
hidden: false,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
group: 'prometheus',
|
||||
version: 'v0',
|
||||
datasource: {
|
||||
name: 'prometheus-uid',
|
||||
},
|
||||
spec: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const saveAsModel = serializer.getSaveAsModel(dashboard, baseOptions);
|
||||
expect(
|
||||
(saveAsModel.elements['panel-1'] as PanelKind).spec.data.spec.queries[0].spec.query.datasource
|
||||
).toEqual({
|
||||
name: 'prometheus-uid',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not fill data source references for query variables when input did contain it', () => {
|
||||
const queryVariable: QueryVariableKind = {
|
||||
kind: 'QueryVariable',
|
||||
spec: {
|
||||
name: 'app',
|
||||
current: {
|
||||
text: 'app1',
|
||||
value: 'app1',
|
||||
},
|
||||
hide: 'dontHide',
|
||||
includeAll: false,
|
||||
label: 'Query Variable',
|
||||
skipUrlSync: false,
|
||||
regex: '',
|
||||
definition: '',
|
||||
options: [],
|
||||
refresh: 'never',
|
||||
sort: 'alphabeticalAsc',
|
||||
multi: false,
|
||||
allowCustomValue: true,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
group: 'prometheus',
|
||||
version: 'v0',
|
||||
spec: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
const dashboard = setupV2({
|
||||
variables: [queryVariable],
|
||||
});
|
||||
const saveAsModel = serializer.getSaveAsModel(dashboard, baseOptions);
|
||||
expect((saveAsModel.variables[0] as QueryVariableKind).spec.query.datasource).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should fill data source references for query variables when input did contain it', () => {
|
||||
const queryVariable: QueryVariableKind = {
|
||||
kind: 'QueryVariable',
|
||||
spec: {
|
||||
name: 'app',
|
||||
current: {
|
||||
text: 'app1',
|
||||
value: 'app1',
|
||||
},
|
||||
hide: 'dontHide',
|
||||
includeAll: false,
|
||||
label: 'Query Variable',
|
||||
skipUrlSync: false,
|
||||
regex: '',
|
||||
definition: '',
|
||||
options: [],
|
||||
refresh: 'never',
|
||||
sort: 'alphabeticalAsc',
|
||||
multi: false,
|
||||
allowCustomValue: true,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
group: 'prometheus',
|
||||
version: 'v0',
|
||||
datasource: {
|
||||
name: 'prometheus-uid',
|
||||
},
|
||||
spec: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
const dashboard = setupV2({
|
||||
variables: [queryVariable],
|
||||
});
|
||||
const saveAsModel = serializer.getSaveAsModel(dashboard, baseOptions);
|
||||
expect((saveAsModel.variables[0] as QueryVariableKind).spec.query.datasource).toEqual({
|
||||
name: 'prometheus-uid',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('panel mapping methods', () => {
|
||||
@@ -1244,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: {} },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1252,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: {} },
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1295,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: {} },
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -1343,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);
|
||||
}
|
||||
}
|
||||
|
||||
+24
-25
@@ -7,19 +7,28 @@ 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",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -28,17 +37,8 @@ exports[`transformSceneToSaveModelSchemaV2 should transform scene to save model
|
||||
"builtIn": false,
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "blue",
|
||||
"name": "query2",
|
||||
"query": {
|
||||
"datasource": {
|
||||
"name": "abcdef",
|
||||
},
|
||||
"group": "prometheus",
|
||||
"kind": "DataQuery",
|
||||
"spec": {},
|
||||
"version": "v0",
|
||||
},
|
||||
"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,226 +0,0 @@
|
||||
import { AnnotationQuery } from '@grafana/data';
|
||||
import { AnnotationQueryKind } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
|
||||
|
||||
import { transformV1ToV2AnnotationQuery, transformV2ToV1AnnotationQuery } from './annotations';
|
||||
|
||||
describe('V1<->V2 annotation convertions', () => {
|
||||
test('given grafana-built in annotations', () => {
|
||||
// test case
|
||||
const annotationDefinition: AnnotationQuery = {
|
||||
builtIn: 1,
|
||||
datasource: {
|
||||
type: 'grafana',
|
||||
uid: 'grafana',
|
||||
},
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'yellow',
|
||||
name: 'Annotations \u0026 Alerts',
|
||||
target: {
|
||||
// @ts-expect-error
|
||||
limit: 100,
|
||||
matchAny: false,
|
||||
tags: [],
|
||||
type: 'dashboard',
|
||||
},
|
||||
type: 'dashboard',
|
||||
};
|
||||
|
||||
const expectedV2: AnnotationQueryKind = {
|
||||
kind: 'AnnotationQuery',
|
||||
spec: {
|
||||
builtIn: true,
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'yellow',
|
||||
name: 'Annotations \u0026 Alerts',
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
group: 'grafana',
|
||||
version: 'v0',
|
||||
datasource: {
|
||||
name: 'grafana',
|
||||
},
|
||||
spec: {
|
||||
limit: 100,
|
||||
matchAny: false,
|
||||
tags: [],
|
||||
type: 'dashboard',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resultV2: AnnotationQueryKind = transformV1ToV2AnnotationQuery(annotationDefinition, 'grafana', 'grafana');
|
||||
|
||||
expect(resultV2).toEqual(expectedV2);
|
||||
|
||||
const resultV1: AnnotationQuery = transformV2ToV1AnnotationQuery(expectedV2);
|
||||
expect(resultV1).toEqual(annotationDefinition);
|
||||
});
|
||||
|
||||
test('given annotations with datasource', () => {
|
||||
const annotationDefinition = {
|
||||
datasource: {
|
||||
type: 'grafana-testdata-datasource',
|
||||
uid: 'uid',
|
||||
},
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'blue',
|
||||
name: 'testdata-annos',
|
||||
target: {
|
||||
lines: 10,
|
||||
refId: 'Anno',
|
||||
scenarioId: 'annotations',
|
||||
},
|
||||
};
|
||||
|
||||
const expectedV2: AnnotationQueryKind = {
|
||||
kind: 'AnnotationQuery',
|
||||
spec: {
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'blue',
|
||||
name: 'testdata-annos',
|
||||
builtIn: false,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
group: 'grafana-testdata-datasource',
|
||||
version: 'v0',
|
||||
datasource: {
|
||||
name: 'uid',
|
||||
},
|
||||
spec: {
|
||||
lines: 10,
|
||||
refId: 'Anno',
|
||||
scenarioId: 'annotations',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resultV2: AnnotationQueryKind = transformV1ToV2AnnotationQuery(
|
||||
annotationDefinition,
|
||||
'grafana-testdata-datasource',
|
||||
'uid'
|
||||
);
|
||||
|
||||
expect(resultV2).toEqual(expectedV2);
|
||||
|
||||
const resultV1: AnnotationQuery = transformV2ToV1AnnotationQuery(expectedV2);
|
||||
expect(resultV1).toEqual(annotationDefinition);
|
||||
});
|
||||
|
||||
test('given annotations with target', () => {
|
||||
const annotationDefinition = {
|
||||
datasource: {
|
||||
type: 'prometheus',
|
||||
uid: 'uid',
|
||||
},
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'yellow',
|
||||
name: 'prom-annos',
|
||||
target: {
|
||||
expr: '{action="add_client"}',
|
||||
interval: '',
|
||||
lines: 10,
|
||||
refId: 'Anno',
|
||||
scenarioId: 'annotations',
|
||||
},
|
||||
};
|
||||
|
||||
const expectedV2: AnnotationQueryKind = {
|
||||
kind: 'AnnotationQuery',
|
||||
spec: {
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'yellow',
|
||||
name: 'prom-annos',
|
||||
builtIn: false,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
group: 'prometheus',
|
||||
version: 'v0',
|
||||
datasource: {
|
||||
name: 'uid',
|
||||
},
|
||||
spec: {
|
||||
expr: '{action="add_client"}',
|
||||
interval: '',
|
||||
lines: 10,
|
||||
refId: 'Anno',
|
||||
scenarioId: 'annotations',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resultV2: AnnotationQueryKind = transformV1ToV2AnnotationQuery(annotationDefinition, 'prometheus', 'uid');
|
||||
expect(resultV2).toEqual(expectedV2);
|
||||
|
||||
const resultV1: AnnotationQuery = transformV2ToV1AnnotationQuery(expectedV2);
|
||||
expect(resultV1).toEqual(annotationDefinition);
|
||||
});
|
||||
|
||||
test('given annotations with non-schematised options / legacyOptions', () => {
|
||||
const annotationDefinition = {
|
||||
datasource: {
|
||||
type: 'elasticsearch',
|
||||
uid: 'uid',
|
||||
},
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'red',
|
||||
name: 'elastic - annos',
|
||||
tagsField: 'asd',
|
||||
target: {
|
||||
lines: 10,
|
||||
query: 'test query',
|
||||
refId: 'Anno',
|
||||
scenarioId: 'annotations',
|
||||
},
|
||||
textField: 'asd',
|
||||
timeEndField: 'asdas',
|
||||
timeField: 'asd',
|
||||
};
|
||||
|
||||
const expectedV2: AnnotationQueryKind = {
|
||||
kind: 'AnnotationQuery',
|
||||
spec: {
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'red',
|
||||
name: 'elastic - annos',
|
||||
builtIn: false,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
group: 'elasticsearch',
|
||||
version: 'v0',
|
||||
datasource: {
|
||||
name: 'uid',
|
||||
},
|
||||
spec: {
|
||||
lines: 10,
|
||||
query: 'test query',
|
||||
refId: 'Anno',
|
||||
scenarioId: 'annotations',
|
||||
},
|
||||
},
|
||||
legacyOptions: {
|
||||
tagsField: 'asd',
|
||||
textField: 'asd',
|
||||
timeEndField: 'asdas',
|
||||
timeField: 'asd',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resultV2: AnnotationQueryKind = transformV1ToV2AnnotationQuery(annotationDefinition, 'elasticsearch', 'uid');
|
||||
expect(resultV2).toEqual(expectedV2);
|
||||
|
||||
const resultV1: AnnotationQuery = transformV2ToV1AnnotationQuery(expectedV2);
|
||||
expect(resultV1).toEqual(annotationDefinition);
|
||||
});
|
||||
});
|
||||
@@ -1,118 +0,0 @@
|
||||
import { AnnotationQuery } from '@grafana/data';
|
||||
import {
|
||||
AnnotationQueryKind,
|
||||
defaultDataQueryKind,
|
||||
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
|
||||
|
||||
import { getRuntimePanelDataSource } from './layoutSerializers/utils';
|
||||
|
||||
export function transformV1ToV2AnnotationQuery(
|
||||
annotation: AnnotationQuery,
|
||||
|
||||
dsType: string,
|
||||
dsUID?: string,
|
||||
// Overrides are used to provide properties based on scene's annotations data layer object state
|
||||
override?: Partial<AnnotationQuery>
|
||||
): AnnotationQueryKind {
|
||||
const group = annotation.builtIn ? 'grafana' : dsType;
|
||||
|
||||
const {
|
||||
// known properties documented in v1 schema
|
||||
enable,
|
||||
hide,
|
||||
iconColor,
|
||||
name,
|
||||
builtIn,
|
||||
filter,
|
||||
mappings,
|
||||
datasource,
|
||||
target,
|
||||
snapshotData,
|
||||
type,
|
||||
|
||||
// unknown properties that are still available for configuration through API
|
||||
...legacyOptions
|
||||
} = annotation;
|
||||
|
||||
const result: AnnotationQueryKind = {
|
||||
kind: 'AnnotationQuery',
|
||||
spec: {
|
||||
builtIn: Boolean(annotation.builtIn),
|
||||
name: annotation.name,
|
||||
enable: Boolean(override?.enable) || Boolean(annotation.enable),
|
||||
hide: Boolean(override?.hide) || Boolean(annotation.hide),
|
||||
iconColor: annotation.iconColor,
|
||||
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
version: defaultDataQueryKind().version,
|
||||
group, // Annotation layer has a datasource type provided in runtime.
|
||||
spec: target || {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (dsUID) {
|
||||
result.spec.query.datasource = {
|
||||
name: dsUID,
|
||||
};
|
||||
}
|
||||
|
||||
// if legacy options is not an empty object, add it to the result
|
||||
if (Object.keys(legacyOptions).length > 0) {
|
||||
result.spec.legacyOptions = legacyOptions;
|
||||
}
|
||||
|
||||
if (annotation.filter?.ids?.length) {
|
||||
result.spec.filter = annotation.filter;
|
||||
}
|
||||
|
||||
// TODO: add mappings
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function transformV2ToV1AnnotationQuery(annotation: AnnotationQueryKind): AnnotationQuery {
|
||||
let { query: dataQuery, ...annotationQuery } = annotation.spec;
|
||||
|
||||
// Mapping from AnnotationQueryKind to AnnotationQuery used by scenes.
|
||||
let annoQuerySpec: AnnotationQuery = {
|
||||
enable: annotation.spec.enable,
|
||||
hide: annotation.spec.hide,
|
||||
iconColor: annotation.spec.iconColor,
|
||||
name: annotation.spec.name,
|
||||
// TOOO: mappings
|
||||
};
|
||||
|
||||
if (Object.keys(dataQuery.spec).length > 0) {
|
||||
// @ts-expect-error DataQueryKind spec should be typed as DataQuery interface
|
||||
annoQuerySpec.target = {
|
||||
...dataQuery?.spec,
|
||||
};
|
||||
}
|
||||
|
||||
if (annotation.spec.builtIn) {
|
||||
annoQuerySpec.type = 'dashboard';
|
||||
annoQuerySpec.builtIn = 1;
|
||||
}
|
||||
|
||||
if (annotation.spec.filter) {
|
||||
annoQuerySpec.filter = annotation.spec.filter;
|
||||
}
|
||||
|
||||
// some annotations will contain in the legacyOptions properties that need to be
|
||||
// added to the root level AnnotationQuery
|
||||
if (annotationQuery.legacyOptions) {
|
||||
annoQuerySpec = {
|
||||
...annoQuerySpec,
|
||||
...annotationQuery.legacyOptions,
|
||||
};
|
||||
}
|
||||
|
||||
// get data source from annotation query
|
||||
const datasource = getRuntimePanelDataSource(dataQuery);
|
||||
|
||||
annoQuerySpec.datasource = datasource;
|
||||
|
||||
return annoQuerySpec;
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
+68
-69
@@ -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,
|
||||
|
||||
+40
-56
@@ -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,35 +764,23 @@ 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',
|
||||
hide: true,
|
||||
type: 'dashboard',
|
||||
});
|
||||
|
||||
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',
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'purple',
|
||||
expr: 'rate(http_requests_total[5m])',
|
||||
queryType: 'range',
|
||||
legendFormat: '{{method}} {{endpoint}}',
|
||||
useValueAsTime: true,
|
||||
step: '1m',
|
||||
});
|
||||
|
||||
// Verify the original legacyOptions object is also preserved
|
||||
expect(annotationLayer.state.query.legacyOptions).toMatchObject({
|
||||
expr: 'rate(http_requests_total[5m])',
|
||||
queryType: 'range',
|
||||
legendFormat: '{{method}} {{endpoint}}',
|
||||
|
||||
+17
-18
@@ -28,7 +28,6 @@ import {
|
||||
defaultAdhocVariableKind,
|
||||
defaultConstantVariableKind,
|
||||
defaultCustomVariableKind,
|
||||
defaultDataQueryKind,
|
||||
defaultDatasourceVariableKind,
|
||||
defaultGroupByVariableKind,
|
||||
defaultIntervalVariableKind,
|
||||
@@ -63,7 +62,6 @@ import { DashboardScene } from '../scene/DashboardScene';
|
||||
import { DashboardLayoutManager } from '../scene/types/DashboardLayoutManager';
|
||||
import { getIntervalsFromQueryString } from '../utils/utils';
|
||||
|
||||
import { transformV2ToV1AnnotationQuery } from './annotations';
|
||||
import { SnapshotVariable } from './custom-variables/SnapshotVariable';
|
||||
import { layoutDeserializerRegistry } from './layoutSerializers/layoutSerializerRegistry';
|
||||
import { getRuntimeVariableDataSource } from './layoutSerializers/utils';
|
||||
@@ -92,23 +90,32 @@ export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo<D
|
||||
const { spec: dashboard, metadata, apiVersion } = dto;
|
||||
|
||||
// annotations might not come with the builtIn Grafana annotation, we need to add it
|
||||
|
||||
const grafanaBuiltAnnotation = getGrafanaBuiltInAnnotationDataLayer(dashboard);
|
||||
if (grafanaBuiltAnnotation) {
|
||||
dashboard.annotations.unshift(grafanaBuiltAnnotation);
|
||||
}
|
||||
|
||||
const annotationLayers = dashboard.annotations.map((annotation) => {
|
||||
const annotationQuerySpec = transformV2ToV1AnnotationQuery(annotation);
|
||||
|
||||
const layerState = {
|
||||
let annoQuerySpec = annotation.spec;
|
||||
// some annotations will contain in the legacyOptions properties that need to be
|
||||
// added to the root level annotation spec
|
||||
if (annoQuerySpec?.legacyOptions) {
|
||||
annoQuerySpec = {
|
||||
...annoQuerySpec,
|
||||
...annoQuerySpec.legacyOptions,
|
||||
};
|
||||
}
|
||||
return new DashboardAnnotationsDataLayer({
|
||||
key: uniqueId('annotations-'),
|
||||
query: annotationQuerySpec,
|
||||
query: {
|
||||
...annoQuerySpec,
|
||||
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);
|
||||
@@ -507,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,
|
||||
|
||||
+35
-50
@@ -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', () => {
|
||||
@@ -561,8 +562,16 @@ describe('transformSceneToSaveModelSchemaV2', () => {
|
||||
name: 'annotation-with-options',
|
||||
enable: true,
|
||||
iconColor: 'red',
|
||||
customProp1: true,
|
||||
customProp2: 'test',
|
||||
legacyOptions: {
|
||||
expr: 'rate(http_requests_total[5m])',
|
||||
queryType: 'range',
|
||||
legendFormat: '{{method}} {{endpoint}}',
|
||||
useValueAsTime: true,
|
||||
},
|
||||
// Some other properties that aren't in the annotation spec
|
||||
// and should be moved to options
|
||||
customProp1: 'value1',
|
||||
customProp2: 'value2',
|
||||
},
|
||||
name: 'layerWithOptions',
|
||||
isEnabled: true,
|
||||
@@ -584,11 +593,19 @@ describe('transformSceneToSaveModelSchemaV2', () => {
|
||||
expect(result.annotations.length).toBe(1);
|
||||
expect(result.annotations[0].spec.legacyOptions).toBeDefined();
|
||||
expect(result.annotations[0].spec.legacyOptions).toEqual({
|
||||
customProp1: true,
|
||||
customProp2: 'test',
|
||||
expr: 'rate(http_requests_total[5m])',
|
||||
queryType: 'range',
|
||||
legendFormat: '{{method}} {{endpoint}}',
|
||||
useValueAsTime: true,
|
||||
customProp1: 'value1',
|
||||
customProp2: 'value2',
|
||||
});
|
||||
|
||||
// Ensure these properties are not at the root level
|
||||
expect(result).not.toHaveProperty('annotations[0].spec.expr');
|
||||
expect(result).not.toHaveProperty('annotations[0].spec.queryType');
|
||||
expect(result).not.toHaveProperty('annotations[0].spec.legendFormat');
|
||||
expect(result).not.toHaveProperty('annotations[0].spec.useValueAsTime');
|
||||
expect(result).not.toHaveProperty('annotations[0].spec.customProp1');
|
||||
expect(result).not.toHaveProperty('annotations[0].spec.customProp2');
|
||||
});
|
||||
@@ -817,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,
|
||||
@@ -1092,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,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
+66
-46
@@ -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';
|
||||
@@ -52,7 +51,6 @@ import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
|
||||
import { getLibraryPanelBehavior, getPanelIdForVizPanel, getQueryRunnerFor, isLibraryPanel } from '../utils/utils';
|
||||
|
||||
import { DSReferencesMapping } from './DashboardSceneSerializer';
|
||||
import { transformV1ToV2AnnotationQuery } from './annotations';
|
||||
import { sceneVariablesSetToSchemaV2Variables } from './sceneVariablesSetToVariables';
|
||||
import { colorIdEnumToColorIdV2, transformCursorSynctoEnum } from './transformToV2TypesUtils';
|
||||
|
||||
@@ -252,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;
|
||||
@@ -260,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),
|
||||
@@ -419,46 +407,78 @@ function getAnnotations(state: DashboardSceneState, dsReferencesMapping?: DSRefe
|
||||
if (!(layer instanceof dataLayers.AnnotationsDataLayer)) {
|
||||
continue;
|
||||
}
|
||||
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,
|
||||
},
|
||||
};
|
||||
|
||||
const datasource = getElementDatasource(layer, layer.state.query, 'annotation', undefined, dsReferencesMapping);
|
||||
|
||||
let layerDs = layer.state.query.datasource;
|
||||
|
||||
if (!layerDs) {
|
||||
// This can happen only if we are transforming a scene that was created
|
||||
// from a v1 spec. In v1 annotation layer can contain no datasource ref, which is guaranteed
|
||||
// for layers created for v2 schema. See transform transformSaveModelSchemaV2ToScene.ts.
|
||||
// In this case we will resolve default data source
|
||||
layerDs = getDefaultDataSourceRef();
|
||||
console.error(
|
||||
'Misconfigured AnnotationsDataLayer: Data source is required for annotations. Resolving default data source',
|
||||
layer,
|
||||
layerDs
|
||||
);
|
||||
// Transform v1 dashboard (using target) to v2 structure
|
||||
// adds extra condition to prioritize query over target
|
||||
// if query is defined, use it
|
||||
if (layer.state.query.target && !layer.state.query.query) {
|
||||
// Handle built-in annotations
|
||||
if (layer.state.query.builtIn) {
|
||||
result.spec.query = {
|
||||
kind: 'grafana', // built-in annotations are always of type grafana
|
||||
spec: {
|
||||
...layer.state.query.target,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
result.spec.query = {
|
||||
kind: getAnnotationQueryKind(layer.state.query),
|
||||
spec: {
|
||||
...layer.state.query.target,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
// For annotations without query.query defined (e.g., grafana annotations without tags)
|
||||
else if (layer.state.query.query?.kind) {
|
||||
result.spec.query = {
|
||||
kind: layer.state.query.query.kind,
|
||||
spec: {
|
||||
...layer.state.query.query.spec,
|
||||
},
|
||||
};
|
||||
}
|
||||
// Collect datasource-specific properties not in standard annotation spec
|
||||
let otherProps = omit(
|
||||
layer.state.query,
|
||||
'type',
|
||||
'target',
|
||||
'builtIn',
|
||||
'name',
|
||||
'datasource',
|
||||
'iconColor',
|
||||
'enable',
|
||||
'hide',
|
||||
'filter',
|
||||
'query'
|
||||
);
|
||||
|
||||
const result = transformV1ToV2AnnotationQuery(layer.state.query, layerDs.type!, layerDs.uid!, {
|
||||
enable: layer.state.isEnabled,
|
||||
hide: layer.state.isHidden,
|
||||
});
|
||||
|
||||
const annotationQuery = layer.state.query;
|
||||
// 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
|
||||
const { legacyOptions, ...restProps } = otherProps;
|
||||
// Merge options with the rest of the properties
|
||||
result.spec.legacyOptions = { ...legacyOptions, ...restProps };
|
||||
}
|
||||
|
||||
// If filter is an empty array, don't save it
|
||||
if (annotationQuery.filter?.ids?.length) {
|
||||
result.spec.filter = annotationQuery.filter;
|
||||
}
|
||||
|
||||
// Finally, if the datasource references mapping did not containt data source ref,
|
||||
// this means that the original model that was fetched did not contain it. In such scenario we don't want to save
|
||||
// the explicit data source reference, so lets remove it from the save model.
|
||||
if (!datasource) {
|
||||
delete result.spec.query.datasource;
|
||||
if (layer.state.query.filter?.ids?.length) {
|
||||
result.spec.filter = layer.state.query.filter;
|
||||
}
|
||||
|
||||
annotations.push(result);
|
||||
}
|
||||
|
||||
return annotations;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ export default function ShareButton({ dashboard, panel }: { dashboard: Dashboard
|
||||
|
||||
const [_, buildUrl] = useAsyncFn(async () => {
|
||||
DashboardInteractions.toolbarShareClick();
|
||||
return await buildShareUrl(dashboard, panel);
|
||||
}, [dashboard]);
|
||||
await buildShareUrl(dashboard, panel);
|
||||
}, [dashboard, panel]);
|
||||
|
||||
const onMenuClick = useCallback((isOpen: boolean) => {
|
||||
if (isOpen) {
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1016,14 +1015,14 @@ describe('ResponseTransformers', () => {
|
||||
|
||||
function validateAnnotation(v1: AnnotationQuery, v2: DashboardV2Spec['annotations'][0]) {
|
||||
const { spec: v2Spec } = v2;
|
||||
|
||||
expect(v1.name).toBe(v2Spec.name);
|
||||
expect(v1.datasource?.type).toBe(v2Spec.query.group);
|
||||
expect(v1.datasource?.uid).toBe(v2Spec.query.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);
|
||||
expect(v1.builtIn).toBe(v2Spec.builtIn ? 1 : undefined);
|
||||
expect(v1.target).toEqual(v2Spec.query.spec);
|
||||
expect(v1.builtIn).toBe(v2Spec.builtIn ? 1 : 0);
|
||||
expect(v1.target).toBe(v2Spec.query?.spec);
|
||||
expect(v1.filter).toEqual(v2Spec.filter);
|
||||
}
|
||||
|
||||
@@ -1044,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,
|
||||
};
|
||||
})
|
||||
@@ -1097,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';
|
||||
@@ -56,7 +55,6 @@ import {
|
||||
DeprecatedInternalId,
|
||||
ObjectMeta,
|
||||
} from 'app/features/apiserver/types';
|
||||
import { transformV2ToV1AnnotationQuery } from 'app/features/dashboard-scene/serialization/annotations';
|
||||
import { GRID_ROW_HEIGHT } from 'app/features/dashboard-scene/serialization/const';
|
||||
import { validateFiltersOrigin } from 'app/features/dashboard-scene/serialization/sceneVariablesSetToVariables';
|
||||
import { TypedVariableModelV2 } from 'app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene';
|
||||
@@ -504,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,
|
||||
},
|
||||
@@ -574,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,
|
||||
@@ -733,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,
|
||||
},
|
||||
@@ -773,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,
|
||||
@@ -893,6 +874,22 @@ function getVariablesV1(vars: DashboardV2Spec['variables']): VariableModel[] {
|
||||
return variables;
|
||||
}
|
||||
|
||||
function getAnnotationsV1(annotations: DashboardV2Spec['annotations']): AnnotationQuery[] {
|
||||
// @ts-expect-error - target v2 query is not compatible with v1 target
|
||||
return annotations.map((a) => {
|
||||
return {
|
||||
name: a.spec.name,
|
||||
datasource: a.spec.datasource,
|
||||
enable: a.spec.enable,
|
||||
hide: a.spec.hide,
|
||||
iconColor: a.spec.iconColor,
|
||||
builtIn: a.spec.builtIn ? 1 : 0,
|
||||
target: a.spec.query?.spec,
|
||||
filter: a.spec.filter,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
interface LibraryPanelDTO extends Pick<Panel, 'libraryPanel' | 'id' | 'title' | 'gridPos' | 'type'> {}
|
||||
|
||||
function getPanelsV1(
|
||||
@@ -953,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,
|
||||
};
|
||||
}),
|
||||
@@ -1140,8 +1134,7 @@ function transformToV1VariableTypes(variable: TypedVariableModelV2): VariableTyp
|
||||
}
|
||||
|
||||
export function transformDashboardV2SpecToV1(spec: DashboardV2Spec, metadata: ObjectMeta): DashboardDataDTO {
|
||||
const annotations = spec.annotations.map(transformV2ToV1AnnotationQuery);
|
||||
|
||||
const annotations = getAnnotationsV1(spec.annotations);
|
||||
const variables = getVariablesV1(spec.variables);
|
||||
const panels = getPanelsV1(spec.elements, spec.layout);
|
||||
return {
|
||||
|
||||
@@ -154,6 +154,7 @@ export const InfiniteScroll = ({
|
||||
displayedFields={displayedFields}
|
||||
index={index}
|
||||
log={logs[index]}
|
||||
logs={logs}
|
||||
onClick={onClick}
|
||||
showTime={showTime}
|
||||
style={style}
|
||||
|
||||
@@ -45,6 +45,7 @@ describe.each(fontSizes)('LogLine', (fontSize: LogListFontSize) => {
|
||||
displayedFields: [],
|
||||
index: 0,
|
||||
log,
|
||||
logs: [log],
|
||||
onClick: jest.fn(),
|
||||
showTime: true,
|
||||
style: {},
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface Props {
|
||||
displayedFields: string[];
|
||||
index: number;
|
||||
log: LogListModel;
|
||||
logs: LogListModel[];
|
||||
showTime: boolean;
|
||||
style: CSSProperties;
|
||||
styles: LogLineStyles;
|
||||
@@ -41,6 +42,7 @@ export const LogLine = ({
|
||||
displayedFields,
|
||||
index,
|
||||
log,
|
||||
logs,
|
||||
style,
|
||||
styles,
|
||||
onClick,
|
||||
@@ -57,6 +59,7 @@ export const LogLine = ({
|
||||
height={style.height}
|
||||
index={index}
|
||||
log={log}
|
||||
logs={logs}
|
||||
styles={styles}
|
||||
onClick={onClick}
|
||||
onOverflow={onOverflow}
|
||||
@@ -79,6 +82,7 @@ const LogLineComponent = memo(
|
||||
height,
|
||||
index,
|
||||
log,
|
||||
logs,
|
||||
styles,
|
||||
onClick,
|
||||
onOverflow,
|
||||
@@ -237,7 +241,7 @@ const LogLineComponent = memo(
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{detailsMode === 'inline' && detailsShown && <InlineLogLineDetails logs={[]} />}
|
||||
{detailsMode === 'inline' && detailsShown && <InlineLogLineDetails logs={logs} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { createLogLineLinks } from '../logParser';
|
||||
import { LogLineDetailsDisplayedFields } from './LogLineDetailsDisplayedFields';
|
||||
import { LabelWithLinks, LogLineDetailsFields, LogLineDetailsLabelFields } from './LogLineDetailsFields';
|
||||
import { LogLineDetailsHeader } from './LogLineDetailsHeader';
|
||||
import { LogLineDetailsLog } from './LogLineDetailsLog';
|
||||
import { useLogListContext } from './LogListContext';
|
||||
import { LogListModel } from './processing';
|
||||
|
||||
@@ -102,7 +103,7 @@ export const LogLineDetailsComponent = ({ log, logs }: LogLineDetailsComponentPr
|
||||
isOpen={logLineOpen}
|
||||
onToggle={(isOpen: boolean) => handleToggle('logLineOpen', isOpen)}
|
||||
>
|
||||
<div className={styles.logLineWrapper}>{log.raw}</div>
|
||||
<LogLineDetailsLog log={log} />
|
||||
</ControlledCollapse>
|
||||
{displayedFields.length > 0 && setDisplayedFields && (
|
||||
<ControlledCollapse
|
||||
@@ -189,8 +190,4 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
componentWrapper: css({
|
||||
padding: theme.spacing(0, 1, 1, 1),
|
||||
}),
|
||||
logLineWrapper: css({
|
||||
maxHeight: '50vh',
|
||||
overflow: 'auto',
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { memo, useMemo } from 'react';
|
||||
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { getStyles } from './LogLine';
|
||||
import { useLogListContext } from './LogListContext';
|
||||
import { LogListModel } from './processing';
|
||||
|
||||
interface Props {
|
||||
log: LogListModel;
|
||||
}
|
||||
|
||||
export const LogLineDetailsLog = memo(({ log: originalLog }: Props) => {
|
||||
const { syntaxHighlighting } = useLogListContext();
|
||||
const logStyles = useStyles2(getStyles);
|
||||
const log = useMemo(() => {
|
||||
const log = originalLog.clone();
|
||||
return log;
|
||||
}, [originalLog]);
|
||||
|
||||
return (
|
||||
<div className={styles.logLineWrapper}>
|
||||
{!syntaxHighlighting ? (
|
||||
<div className="field no-highlighting">{log.body}</div>
|
||||
) : (
|
||||
<div className={logStyles.logLine}>
|
||||
<div className={logStyles.wrappedLogLine}>
|
||||
<div className="field log-syntax-highlight" dangerouslySetInnerHTML={{ __html: log.highlightedBody }} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
LogLineDetailsLog.displayName = 'LogLineDetailsLog';
|
||||
|
||||
const styles = {
|
||||
logLineWrapper: css({
|
||||
maxHeight: '50vh',
|
||||
overflow: 'auto',
|
||||
}),
|
||||
};
|
||||
@@ -140,6 +140,36 @@ describe('preProcessLogs', () => {
|
||||
);
|
||||
expect(logListModel.getDisplayedFieldValue(LOG_LINE_BODY_FIELD_NAME, true)).toBe('log message 1');
|
||||
});
|
||||
|
||||
test('Prettifies JSON', () => {
|
||||
const entry = '{"key": "value", "otherKey": "otherValue"}';
|
||||
const logListModel = createLogLine(
|
||||
{ entry },
|
||||
{
|
||||
escape: false,
|
||||
order: LogsSortOrder.Descending,
|
||||
timeZone: 'browser',
|
||||
wrapLogMessage: false, // unwrapped
|
||||
}
|
||||
);
|
||||
expect(logListModel.entry).toBe(entry);
|
||||
expect(logListModel.body).not.toBe(entry);
|
||||
});
|
||||
|
||||
test('Uses lossless parsing', () => {
|
||||
const entry = '{"number": 90071992547409911}';
|
||||
const logListModel = createLogLine(
|
||||
{ entry },
|
||||
{
|
||||
escape: false,
|
||||
order: LogsSortOrder.Descending,
|
||||
timeZone: 'browser',
|
||||
wrapLogMessage: false, // unwrapped
|
||||
}
|
||||
);
|
||||
expect(logListModel.entry).toBe(entry);
|
||||
expect(logListModel.body).toContain('90071992547409911');
|
||||
});
|
||||
});
|
||||
|
||||
test('Orders logs', () => {
|
||||
@@ -243,6 +273,35 @@ describe('preProcessLogs', () => {
|
||||
expect(entry).toContain(longLog.body);
|
||||
});
|
||||
|
||||
test('Sets the collapsed state based on the new lines count', () => {
|
||||
const entry = new Array(virtualization.getTruncationLineCount()).fill('test\n').join('');
|
||||
const multilineLog = createLogLine(
|
||||
{ entry, labels: { field: 'value' } },
|
||||
{ escape: false, order: LogsSortOrder.Descending, timeZone: 'browser', virtualization, wrapLogMessage: true }
|
||||
);
|
||||
|
||||
expect(multilineLog.collapsed).toBeUndefined();
|
||||
|
||||
multilineLog.updateCollapsedState([], container);
|
||||
|
||||
expect(multilineLog.collapsed).toBe(true);
|
||||
expect(entry).toContain(multilineLog.body);
|
||||
});
|
||||
|
||||
test('Correctly counts new lines', () => {
|
||||
const entry = new Array(virtualization.getTruncationLineCount() - 1).fill('test\n').join('');
|
||||
const multilineLog = createLogLine(
|
||||
{ entry, labels: { field: 'value' } },
|
||||
{ escape: false, order: LogsSortOrder.Descending, timeZone: 'browser', virtualization, wrapLogMessage: true }
|
||||
);
|
||||
|
||||
expect(multilineLog.collapsed).toBeUndefined();
|
||||
|
||||
multilineLog.updateCollapsedState([], container);
|
||||
|
||||
expect(multilineLog.collapsed).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Considers the displayed fields to set the collapsed state', () => {
|
||||
// Make container half of the size
|
||||
jest.spyOn(container, 'clientWidth', 'get').mockReturnValue(100);
|
||||
@@ -255,6 +314,22 @@ describe('preProcessLogs', () => {
|
||||
expect(longLog.collapsed).toBeUndefined();
|
||||
});
|
||||
|
||||
test('Considers new lines in displayed fields to set the collapsed state', () => {
|
||||
const entry = new Array(virtualization.getTruncationLineCount() - 1).fill('test\n').join('');
|
||||
const field = 'test\n';
|
||||
const multilineLog = createLogLine(
|
||||
{ entry, labels: { field } },
|
||||
{ escape: false, order: LogsSortOrder.Descending, timeZone: 'browser', virtualization, wrapLogMessage: true }
|
||||
);
|
||||
|
||||
expect(multilineLog.collapsed).toBeUndefined();
|
||||
|
||||
multilineLog.updateCollapsedState(['field', LOG_LINE_BODY_FIELD_NAME], container);
|
||||
|
||||
expect(multilineLog.collapsed).toBe(true);
|
||||
expect(entry).toContain(multilineLog.body);
|
||||
});
|
||||
|
||||
test('Updates the body based on the collapsed state', () => {
|
||||
expect(longLog.collapsed).toBeUndefined();
|
||||
expect(longLog.body).toBe(entry);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import ansicolor from 'ansicolor';
|
||||
import { parse, stringify } from 'lossless-json';
|
||||
import Prism, { Grammar } from 'prismjs';
|
||||
|
||||
import { DataFrame, dateTimeFormat, Labels, LogLevel, LogRowModel, LogsSortOrder } from '@grafana/data';
|
||||
import { DataFrame, dateTimeFormat, Labels, LogLevel, LogRowModel, LogsSortOrder, textUtil } from '@grafana/data';
|
||||
import { GetFieldLinksFn } from 'app/plugins/panel/logs/types';
|
||||
|
||||
import { checkLogsError, checkLogsSampled, escapeUnescapedString, sortLogRows } from '../../utils';
|
||||
@@ -97,8 +98,23 @@ export class LogListModel implements LogRowModel {
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
clone() {
|
||||
const clone = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
|
||||
// Unless this function is required outside of <LogLineDetailsLog />, we create a wrapped clone, so new lines are not stripped.
|
||||
clone._wrapLogMessage = true;
|
||||
clone._body = undefined;
|
||||
clone._highlightedBody = undefined;
|
||||
return clone;
|
||||
}
|
||||
|
||||
get body(): string {
|
||||
if (this._body === undefined) {
|
||||
try {
|
||||
const parsed = stringify(parse(this.raw), undefined, 2);
|
||||
if (parsed) {
|
||||
this.raw = parsed;
|
||||
}
|
||||
} catch (error) {}
|
||||
this._body = this.collapsed
|
||||
? this.raw.substring(0, this._virtualization?.getTruncationLength(null) ?? TRUNCATION_DEFAULT_LENGTH)
|
||||
: this.raw;
|
||||
@@ -124,7 +140,11 @@ export class LogListModel implements LogRowModel {
|
||||
if (this._highlightedBody === undefined) {
|
||||
this._grammar = this._grammar ?? generateLogGrammar(this);
|
||||
const extraGrammar = generateTextMatchGrammar(this.searchWords, this._currentSearch);
|
||||
this._highlightedBody = Prism.highlight(this.body, { ...extraGrammar, ...this._grammar }, 'lokiql');
|
||||
this._highlightedBody = Prism.highlight(
|
||||
textUtil.sanitize(this.body),
|
||||
{ ...extraGrammar, ...this._grammar },
|
||||
'lokiql'
|
||||
);
|
||||
}
|
||||
return this._highlightedBody;
|
||||
}
|
||||
@@ -154,16 +174,27 @@ export class LogListModel implements LogRowModel {
|
||||
}
|
||||
|
||||
updateCollapsedState(displayedFields: string[], container: HTMLDivElement | null) {
|
||||
const lineLength =
|
||||
const line =
|
||||
displayedFields.length > 0
|
||||
? displayedFields.map((field) => this.getDisplayedFieldValue(field, true)).join('').length
|
||||
: this.entry.length;
|
||||
const collapsed =
|
||||
lineLength >= (this._virtualization?.getTruncationLength(container) ?? TRUNCATION_DEFAULT_LENGTH)
|
||||
? displayedFields.map((field) => this.getDisplayedFieldValue(field, true)).join('')
|
||||
: this.body;
|
||||
|
||||
// Length truncation
|
||||
let collapsed =
|
||||
line.length >= (this._virtualization?.getTruncationLength(container) ?? TRUNCATION_DEFAULT_LENGTH)
|
||||
? true
|
||||
: undefined;
|
||||
|
||||
// Newlines truncation
|
||||
if (!collapsed && this._virtualization) {
|
||||
const truncationLimit = this._virtualization.getTruncationLineCount();
|
||||
collapsed = countNewLines(line, truncationLimit) >= truncationLimit ? true : collapsed;
|
||||
}
|
||||
|
||||
if (this.collapsed === undefined || collapsed === undefined) {
|
||||
this.collapsed = collapsed;
|
||||
this._body = undefined;
|
||||
this._highlightedBody = undefined;
|
||||
}
|
||||
return this.collapsed;
|
||||
}
|
||||
@@ -226,3 +257,23 @@ function logLevelToDisplayLevel(level = '') {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
|
||||
function countNewLines(log: string, limit = Infinity) {
|
||||
let count = 0;
|
||||
for (let i = 0; i < log.length; ++i) {
|
||||
// No need to iterate further
|
||||
if (count > Infinity) {
|
||||
return count;
|
||||
}
|
||||
if (log[i] === '\n') {
|
||||
count += 1;
|
||||
} else if (log[i] === '\r') {
|
||||
count += 1;
|
||||
// skip LF in CRLF
|
||||
if (log[i] === '\n') {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ export class LogLineVirtualization {
|
||||
getGridSize = () => this.gridSize;
|
||||
getPaddingBottom = () => this.paddingBottom;
|
||||
|
||||
// 2/3 of the viewport height
|
||||
getTruncationLineCount = () => Math.round(window.innerHeight / this.getLineHeight() / 1.5);
|
||||
|
||||
getTruncationLength = (container: HTMLDivElement | null) => {
|
||||
|
||||
@@ -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 });
|
||||
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,
|
||||
};
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -18355,6 +18355,7 @@ __metadata:
|
||||
leven: "npm:^4.0.0"
|
||||
lodash: "npm:4.17.21"
|
||||
logfmt: "npm:^1.3.2"
|
||||
lossless-json: "npm:^4.1.1"
|
||||
lru-cache: "npm:11.1.0"
|
||||
lru-memoize: "npm:^1.1.0"
|
||||
lucene: "npm:^2.1.1"
|
||||
@@ -22160,6 +22161,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lossless-json@npm:^4.1.1":
|
||||
version: 4.1.1
|
||||
resolution: "lossless-json@npm:4.1.1"
|
||||
checksum: 10/1e22e9e4ad8ebf169567a8c24553403bfd7abc33c141f4918f1b9626417f80477b849e6175a315a224e0c68072759c69a0e332a925de80269a7f9720af3a7fbd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"loupe@npm:^3.1.0, loupe@npm:^3.1.1, loupe@npm:^3.1.2":
|
||||
version: 3.1.3
|
||||
resolution: "loupe@npm:3.1.3"
|
||||
|
||||
Reference in New Issue
Block a user