mirror of
https://github.com/grafana/grafana.git
synced 2026-01-15 05:35:41 +00:00
Compare commits
13 Commits
wb/plugins
...
v12.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccd7b6ce7e | ||
|
|
1fdeca1015 | ||
|
|
6be2d4128f | ||
|
|
1194671ac0 | ||
|
|
bd602fd6d9 | ||
|
|
f1c1311961 | ||
|
|
ea7eb59bac | ||
|
|
0b985cfd8d | ||
|
|
7d65c5c2a6 | ||
|
|
df07305465 | ||
|
|
f25d2fe7e2 | ||
|
|
0417533b61 | ||
|
|
6d4a443aaf |
39
.drone.yml
39
.drone.yml
@@ -193,7 +193,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all
|
||||
@@ -661,7 +661,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all
|
||||
@@ -1757,7 +1757,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GRAFANA_DIR=$$(pwd)
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- ./pkg/build/daggerbuild/scripts/drone_build_main.sh
|
||||
@@ -1831,13 +1831,30 @@ platform:
|
||||
os: linux
|
||||
services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $(/usr/bin/github-app-external-token) > /github-app/token
|
||||
environment:
|
||||
GITHUB_APP_ID:
|
||||
from_secret: github-app-app-id
|
||||
GITHUB_APP_INSTALLATION_ID:
|
||||
from_secret: github-app-installation-id
|
||||
GITHUB_APP_PRIVATE_KEY:
|
||||
from_secret: github-app-private-key
|
||||
failure: ignore
|
||||
image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59
|
||||
name: github-app-generate-token
|
||||
volumes:
|
||||
- name: github-app
|
||||
path: /github-app
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GRAFANA_DIR=$$(pwd)
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- ./pkg/build/daggerbuild/scripts/drone_build_tag_grafana.sh
|
||||
depends_on:
|
||||
- github-app-generate-token
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
@@ -1889,6 +1906,10 @@ volumes:
|
||||
- host:
|
||||
path: /var/run/docker.sock
|
||||
name: docker
|
||||
- name: github-app
|
||||
path: /github-app
|
||||
- name: github-app
|
||||
temp: {}
|
||||
---
|
||||
clone:
|
||||
retries: 3
|
||||
@@ -1953,7 +1974,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GRAFANA_DIR=$$(pwd)
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- ./pkg/build/daggerbuild/scripts/drone_build_tag_grafana.sh
|
||||
@@ -2060,7 +2081,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GRAFANA_DIR=$$(pwd)
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- ./pkg/build/daggerbuild/scripts/drone_build_nightly_grafana.sh
|
||||
@@ -2204,7 +2225,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GRAFANA_DIR=$$(pwd)
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- ./pkg/build/daggerbuild/scripts/drone_publish_nightly_grafana.sh
|
||||
@@ -2333,7 +2354,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- dagger run --silent go run ./pkg/build/cmd artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF}
|
||||
--enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --build-id=$${DRONE_BUILD_NUMBER}
|
||||
@@ -2986,6 +3007,6 @@ kind: secret
|
||||
name: gcr_credentials
|
||||
---
|
||||
kind: signature
|
||||
hmac: 1198b1489e48a9ced211633a0325d112814553246847fc7320fb5ac2bcb32b7d
|
||||
hmac: dce4ef9b8d45f32e1b2153b8418f668144325ed9fc0c51f2f8245fb091a4cba2
|
||||
|
||||
...
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -10,6 +10,7 @@ RoleSpec: {
|
||||
|
||||
// Display name of the role
|
||||
title: string
|
||||
description: string
|
||||
|
||||
version: int
|
||||
group: string
|
||||
|
||||
@@ -18,9 +18,10 @@ func NewCoreRolespecPermission() *CoreRolespecPermission {
|
||||
// +k8s:openapi-gen=true
|
||||
type CoreRoleSpec struct {
|
||||
// Display name of the role
|
||||
Title string `json:"title"`
|
||||
Version int64 `json:"version"`
|
||||
Group string `json:"group"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Version int64 `json:"version"`
|
||||
Group string `json:"group"`
|
||||
// TODO:
|
||||
// delegatable?: bool
|
||||
// created?
|
||||
|
||||
@@ -18,9 +18,10 @@ func NewGlobalRolespecPermission() *GlobalRolespecPermission {
|
||||
// +k8s:openapi-gen=true
|
||||
type GlobalRoleSpec struct {
|
||||
// Display name of the role
|
||||
Title string `json:"title"`
|
||||
Version int64 `json:"version"`
|
||||
Group string `json:"group"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Version int64 `json:"version"`
|
||||
Group string `json:"group"`
|
||||
// TODO:
|
||||
// delegatable?: bool
|
||||
// created?
|
||||
|
||||
@@ -18,9 +18,10 @@ func NewRolespecPermission() *RolespecPermission {
|
||||
// +k8s:openapi-gen=true
|
||||
type RoleSpec struct {
|
||||
// Display name of the role
|
||||
Title string `json:"title"`
|
||||
Version int64 `json:"version"`
|
||||
Group string `json:"group"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Version int64 `json:"version"`
|
||||
Group string `json:"group"`
|
||||
// TODO:
|
||||
// delegatable?: bool
|
||||
// created?
|
||||
|
||||
@@ -186,6 +186,13 @@ func schema_pkg_apis_iam_v0alpha1_CoreRoleSpec(ref common.ReferenceCallback) com
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"description": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"version": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: 0,
|
||||
@@ -215,7 +222,7 @@ func schema_pkg_apis_iam_v0alpha1_CoreRoleSpec(ref common.ReferenceCallback) com
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"title", "version", "group", "permissions"},
|
||||
Required: []string{"title", "description", "version", "group", "permissions"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
@@ -740,6 +747,13 @@ func schema_pkg_apis_iam_v0alpha1_GlobalRoleSpec(ref common.ReferenceCallback) c
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"description": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"version": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: 0,
|
||||
@@ -769,7 +783,7 @@ func schema_pkg_apis_iam_v0alpha1_GlobalRoleSpec(ref common.ReferenceCallback) c
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"title", "version", "group", "permissions"},
|
||||
Required: []string{"title", "description", "version", "group", "permissions"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
@@ -1600,6 +1614,13 @@ func schema_pkg_apis_iam_v0alpha1_RoleSpec(ref common.ReferenceCallback) common.
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"description": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"version": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: 0,
|
||||
@@ -1629,7 +1650,7 @@ func schema_pkg_apis_iam_v0alpha1_RoleSpec(ref common.ReferenceCallback) common.
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"title", "version", "group", "permissions"},
|
||||
Required: []string{"title", "description", "version", "group", "permissions"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -78,18 +78,10 @@ refs:
|
||||
|
||||
# Create and manage reports
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
|
||||
The redesigned reporting feature is currently in public preview. Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available. To use this feature, enable the `newShareReportDrawer` feature toggle in your Grafana configuration file or, for Grafana Cloud, contact Support.
|
||||
|
||||
{{< /admonition >}}
|
||||
|
||||
**Reporting** allows you to send automated and scheduled emails from any of your dashboards.
|
||||
You can configure several elements of these reports and generate PDFs and CSV files.
|
||||
You can configure several elements of these reports and generate PDFs, CSV files, and embedded images.
|
||||
Any changes you make to a dashboard used in a report are reflected the next time the report is sent.
|
||||
|
||||
{{< figure src="/media/docs/grafana/dashboards/screenshot-report-config-v12.0.png" max-width="600px" alt="The report configuration screen" >}}
|
||||
|
||||
## Requirements
|
||||
|
||||
For Grafana Enterprise, the Reporting feature has the following requirements:
|
||||
@@ -116,89 +108,28 @@ Refer to specific guides to understand what permissions are required.
|
||||
|
||||
## Create a report
|
||||
|
||||
The report creation process is multi-step, but you don't need to complete these steps in order.
|
||||
The report creation process is multi-step, but you don't need to complete these steps in order and you can skip steps by clicking a step name at the top of the page.
|
||||
|
||||
You can also save the report as a draft at any point during the initial creation process.
|
||||
You can also save the report as a draft at any step in the process:
|
||||
|
||||
You can create directly from a dashboard or from the **Reporting** page.
|
||||
Select one of the following tabs for directions on each option.
|
||||

|
||||
|
||||
To create a report, follow these steps:
|
||||
|
||||
{{< tabs >}}
|
||||
{{< tab-content name="Create a report directly from a dashboard" >}}
|
||||
|
||||
1. In the main menu, click **Dashboards**.
|
||||
1. Navigate to the dashboard from which you want to create a report.
|
||||
1. Click the **Share** drop-down list in the top-right corner of the dashboard.
|
||||
1. Click **Schedule report**.
|
||||
|
||||
The **Schedule report** drawer opens. Any other reports using this dashboard are listed in the drawer. You can also click **See all reports** to navigate to **Reporting** for a full list of reports generated from all dashboards.
|
||||
|
||||
1. Click **+ Create a new report**.
|
||||
1. Update the name of the report, if needed.
|
||||
|
||||
By default, the report name is the name of the dashboard.
|
||||
|
||||
1. Expand and complete each section of the report, as needed:
|
||||
- [Dashboards](#1-dashboards)
|
||||
- [Schedule](#2-schedule)
|
||||
- [Email settings](#3-email-settings)
|
||||
- [Recipients](#4-recipients)
|
||||
- [Attachments](#5-attachments)
|
||||
1. Click one of the following buttons at the bottom of the **Schedule report** drawer:
|
||||
|
||||
- The menu icon to access the following options:
|
||||
- **Download CSV**
|
||||
- **Preview PDF**
|
||||
- **Report settings** - Takes you to **Reporting** in a new browser tab and opens the **Report template settings** drawer, where you can configure organization-level report settings.
|
||||
- **Send preview** - Send a preview of the report to your desired recipient. You can choose to use the report recipients:
|
||||
|
||||
{{< figure src="/media/docs/grafana/dashboards/screenshot-send-preview-v12.0.png" max-width="350px" alt="The Send preview modal" >}}
|
||||
|
||||
- **Schedule report** - The report is sent according the schedule you've set.
|
||||
- **Save draft** - You can save a draft at any point during the initial report creation process, even if it's missing required fields. The report won't be sent according to its schedule while it's a draft.
|
||||
|
||||
If you click the **x** at the top of the drawer without scheduling or saving the report as a draft, the report is discarded. This action can't be reversed.
|
||||
|
||||
1. When you finish configuring the report, click the **x** at the top of the **Schedule report** drawer to close it.
|
||||
|
||||
{{< /tab-content >}}
|
||||
{{< tab-content name="Create a report from Reporting" >}}
|
||||
|
||||
1. In the main menu, click **Dashboards > Reporting**.
|
||||
1. Click **+ Create a new report**.
|
||||
1. Complete the report steps, as needed:
|
||||
- [Select dashboard](#1-select-dashboard)
|
||||
- [Format report](#2-format-report)
|
||||
- [Schedule](#3-schedule)
|
||||
- [Share](#4-share)
|
||||
- [Confirm](#5-confirm)
|
||||
1. Click one of the following buttons in the top-right corner of the screen:
|
||||
- **Send now** or **Schedule send** - The report is sent according the schedule you've set.
|
||||
- **Save as draft** - You can save a draft at any point during the report creation or update process, even if it's missing required fields. The report won't be sent according to its schedule while it's a draft.
|
||||
- **Discard** - Delete the report draft. This action can't be reversed.
|
||||
|
||||
The **Schedule report** drawer opens.
|
||||
|
||||
1. Enter a name for the report.
|
||||
1. Expand and complete each section of the report, as needed:
|
||||
- [Dashboards](#1-dashboards)
|
||||
- [Schedule](#2-schedule)
|
||||
- [Email settings](#3-email-settings)
|
||||
- [Recipients](#4-recipients)
|
||||
- [Attachments](#5-attachments)
|
||||
1. Click one of the following buttons at the bottom of the **Schedule report** drawer:
|
||||
|
||||
- The menu icon to access the following options:
|
||||
- **Download CSV**
|
||||
- **Preview PDF**
|
||||
- **Report settings** - Opens the **Report template settings** drawer, where you can configure organization-level report settings.
|
||||
- **Send preview** - Send a preview of the report to your desired recipient. You can choose to use the report recipients:
|
||||
|
||||
{{< figure src="/media/docs/grafana/dashboards/screenshot-send-preview-v12.0.png" max-width="350px" alt="The Send preview modal" >}}
|
||||
|
||||
- **Schedule report** - The report is sent according the schedule you've set.
|
||||
- **Save draft** - Save a draft at any point during the initial report creation process, even if it's missing required fields. The report won't be sent according to its schedule while it's a draft.
|
||||
|
||||
If you click the **x** at the top of the drawer without scheduling or saving the report as a draft, the report is discarded. This action can't be reversed.
|
||||
|
||||
1. When you finish configuring the report, click the **x** at the top of the **Schedule report** drawer to close it.
|
||||
|
||||
{{< /tab-content >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### 1. Dashboards
|
||||
### 1. Select dashboard
|
||||
|
||||
At this step, select the dashboard or dashboards on which the report is based, as well as the variables and time ranges for those dashboards.
|
||||
The options are:
|
||||
@@ -207,53 +138,35 @@ The options are:
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ----------- |
|
||||
| Source dashboard (required) | Select or update the dashboard from which you want to generate the report. If you've created your report directly from a dashboard, this field is already filled in with the name of the current dashboard. |
|
||||
| [Time range](#time-range) | Update the report time range. If you've created the report directly from a dashboard, the default time range is that of the dashboard. Otherwise, the default time range is **Last 6 hours**. |
|
||||
| [Customize template variables](#customize-template-variables) | Select and customize the variable values for the selected dashboard. This section is only displayed if the dashboard has variables. |
|
||||
| + Add dashboard | Add more dashboards to the report. |
|
||||
| Source dashboard (required) | Select the dashboard from which you want to generate the report. |
|
||||
| [Template variables](#template-variables) | Select the variable values for the selected dashboard. This option is only displayed if the dashboard has variables. |
|
||||
| [Time range](#time-range) | If you leave the field empty, reports use the saved time range of the dashboard. Optionally, you can change the time range of the report. |
|
||||
| Add another dashboard | Add more dashboards to the report. |
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
#### Template variables
|
||||
|
||||
This option is only displayed if the dashboard has variables.
|
||||
|
||||
You can configure report-specific template variables for the dashboard on the report page.
|
||||
The variables that you select override the variables from the dashboard.
|
||||
For detailed information about using template variables, refer to [Variables](ref:templates-and-variables).
|
||||
|
||||
The query variables saved with a report might become out-of-date if the results of that query change.
|
||||
For example, if your template variable queries for a list of hostnames and a new hostname is added, then it won't be included in the report.
|
||||
If that occurs, the selected variables must be manually updated in the report.
|
||||
If you select the **All** value for the template variable or if you keep the dashboard's original variable selection, then the report stays up-to-date as new values are added.
|
||||
|
||||
#### Time range
|
||||
|
||||
If you leave the **Time range** field empty, reports use the saved time range of the dashboard.
|
||||
Optionally, you can change the time range of the report by setting it in the **Time range** field.
|
||||
If specified, the custom time range overrides the time range from the report's dashboard.
|
||||
|
||||
#### Customize template variables
|
||||
The page header of the report displays the time range for the dashboard's data queries.
|
||||
|
||||
Configure report-specific template variables for the dashboard.
|
||||
The variables that you select override the variables from the dashboard.
|
||||
For detailed information about using template variables, refer to [Variables](ref:templates-and-variables).
|
||||
|
||||
The query variables saved with a report might become out of date if the results of that query change.
|
||||
For example, if your template variable queries for a list of hostnames and a new hostname is added, then it won't be included in the report.
|
||||
If that occurs, the selected variables must be manually updated in the report.
|
||||
If you select the **All** value for the template variable or if you keep the dashboard's original variable selection, then the report stays up-to-date as new values are added.
|
||||
|
||||
This option is only displayed if the dashboard has variables.
|
||||
|
||||
### 2. Schedule
|
||||
|
||||
At this step, set scheduling information.
|
||||
Options vary depending on the frequency you select.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ----------- |
|
||||
| Schedule | Choose one of the following:<ul><li>**Send now** sends the report immediately after you save it. To stop sending the report at some point in the future, add an end date.</li><li>**Send later** schedules a report for a later date. When you select this option, the required **Start date**, **Start time**, and **Time zone** options are displayed.</li></ul> |
|
||||
| Frequency | You can schedule reports to be sent once, repeated on an hourly, daily, weekly, or monthly basis, or sent at custom intervals. |
|
||||
| Start date | Set the date when the report should start being sent. |
|
||||
| Start time | Set the time when the report should start being sent. |
|
||||
| [Time zone](#time-zone) | Set the time zone of the report. |
|
||||
| End date | Set the date when the report should stop being sent. If you leave this field empty, the report is sent out indefinitely. |
|
||||
| Send only from Monday to Friday | For reports that have an hourly or daily frequency, you can choose to send them only from Monday to Friday. |
|
||||
| Send on the last day of the month | When you schedule a report with a monthly frequency, and set the start date between the 29th and the 31st of the month, the report is only sent during the months that have those dates. If you want the report to be sent every month, select the **Send on the last day of the month** option. This way, the report is sent on the last day of every month regardless of how many days there are in the month. |
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
#### Time zone
|
||||
##### Report time zones
|
||||
|
||||
Reports use the time zone of the dashboard from which they're generated.
|
||||
You can control the time zone for your reports by setting the dashboard to a specific time zone.
|
||||
@@ -265,54 +178,34 @@ If you want to use a specific time zone, save the dashboard with a fixed time zo
|
||||
|
||||
Each dashboard's time zone setting is visible in the [time range controls](ref:time-range-controls).
|
||||
|
||||
### 3. Email settings
|
||||
### 2. Format report
|
||||
|
||||
At this step, configure the report email:
|
||||
|
||||
<!-- vale Grafana.GoogleLyHyphens = NO -->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ----------- |
|
||||
| Email subject | If you leave this field empty, the report name is used as the email subject line. |
|
||||
| Message | The body of the message in the report email. |
|
||||
| Reply-to-email address | The address that appears in the **Reply to** field of the email. |
|
||||
| Include a dashboard link | Include links to the dashboards in the report email. |
|
||||
| Embed dashboard image | The report email is sent with an images of the dashboards embedded in it so recipients see them at a glance. |
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- vale Grafana.GoogleLyHyphens = YES -->
|
||||
|
||||
### 4. Recipients
|
||||
|
||||
Enter the email addresses of the people or teams that you want to receive the report, separated by commas or semicolons.
|
||||
|
||||
### 5. Attachments
|
||||
|
||||
At this step, select one or more report attachment options.
|
||||
At this step, select one or more report formatting options.
|
||||
You can select multiple options, but you must select _at least one_:
|
||||
|
||||
- **Attach the report as a PDF** - Attach the report as one PDF file.
|
||||
- **[Attach a separate PDF of table data](#table-data-in-pdf)** - Attach a separate PDF file to the report email for each table panel on the selected dashboard. Public preview only.
|
||||
- **Attach a CSV file of table panel data** - Attach a CSV file to the report email for each table panel on the selected dashboard.
|
||||
- [Attach the report as a PDF](#attach-the-report-as-a-pdf)
|
||||
- [Include table data as PDF appendix](#table-data-in-pdf) (Public preview only)
|
||||
- [Embed a dashboard image in the email](#embed-a-dashboard-as-an-image-in-the-email)
|
||||
- [Attach a CSV file of the table panel data](#attach-a-csv-file-of-the-table-panel-data)
|
||||
- [Attach a separate PDF of table data](#table-data-in-pdf) (Public preview only)
|
||||
|
||||
#### PDF format
|
||||
#### Attach the report as a PDF
|
||||
|
||||
If you selected a PDF attachment, configure the following formatting options:
|
||||
If you selected the PDF format option under the **Style the PDF** section, you can configure the following options:
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
- **Configure multiple PDFs** - Click the **Combine all dashboard PDFs in one file** checkbox if you want to generate one PDF file for all the dashboards included in the report. This option is only displayed if your report includes multiple dashboards.
|
||||
- **Configure report header** - Click the **Show template variables** checkbox to show dashboard variables.
|
||||
- **Orientation** - Set the report orientation in **Portrait** or **Landscape**. Refer to the [Layout and orientation table](#layout-and-orientation) to see examples.
|
||||
- **Layout** - Select one of the following:
|
||||
|
||||
| Option | Description |
|
||||
|---------------------------------|-------------------------------------------------------------------------------------------------|
|
||||
| Orientation | Set the report orientation in **Portrait** or **Landscape**. Refer to the [Layout and orientation table](#layout-and-orientation) to see examples. |
|
||||
| Layout | Select one of the following:<ul><li>**Simple** - Renders each panel as full-width across the PDF.</li><li>**Grid** - Renders the PDF with the same panel arrangement and width as the source dashboard.</li></ul>Refer to the [Layout and orientation table](#layout-and-orientation) to see examples. |
|
||||
| Zoom | Zoom in to enlarge text in your PDF or zoom out to see more data (like table columns) per panel. |
|
||||
| Combine all dashboard PDFs in one file | Click the checkbox if you want to generate one PDF file for all the dashboards included in the report. This option is only displayed if there are multiple dashboards in the report. |
|
||||
| Show template variables | Click the checkbox to show dashboard variables. This option is only displayed if the report contains variables. |
|
||||
| [Include table data as PDF appendix](#table-data-in-pdf) | Add an appendix of the dashboard table data to the report PDF. This is useful when there's more data in your table visualization than can be shown in the dashboard PDF. _Public preview only._ |
|
||||
<!-- prettier-ignore-end -->
|
||||
- **Simple** - Renders each panel as full-width across the PDF.
|
||||
- **Grid** - Renders the PDF with the same panel arrangement and width as the source dashboard.
|
||||
|
||||
Refer to the [Layout and orientation table](#layout-and-orientation) to see examples.
|
||||
|
||||
- **Zoom** - Zoom in to enlarge text in your PDF, or zoom out to see more data (like table columns) per panel.
|
||||
|
||||
Click **Preview PDF** in the top-right corner of the screen to view a rendered PDF with the options you selected.
|
||||
|
||||
##### Layout and orientation
|
||||
|
||||
@@ -327,6 +220,17 @@ If you selected a PDF attachment, configure the following formatting options:
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
#### Embed a dashboard as an image in the email
|
||||
|
||||
You can send a report email with an image of the dashboard embedded in the email.
|
||||
This lets the email recipients see the dashboard at a glance.
|
||||
|
||||
#### Attach a CSV file of the table panel data
|
||||
|
||||
You can attach a CSV file to the report email for each table panel on the selected dashboard.
|
||||
|
||||
Click **Download CSV** in the top-right corner of the screen to download a zipped file of the CSV files for your selected dashboard.
|
||||
|
||||
#### Table data in PDF
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
@@ -338,66 +242,94 @@ When there's more data in your table visualizations than can be shown in the das
|
||||
- **Include table data as PDF appendix** - Adds an appendix to the dashboard PDF.
|
||||
- **Attach a separate PDF of table data** - Generates a separate PDF file.
|
||||
|
||||
### 3. Schedule
|
||||
|
||||
At this step, set scheduling information.
|
||||
Options vary depending on the frequency you select.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ----------- |
|
||||
| Frequency | You can schedule reports to be sent once, or repeated on an hourly, daily, weekly, or monthly basis, or sent at custom intervals. You can also disable scheduling by selecting **Never**. For example, you might want to [send the report using the API](#send-a-report-using-the-api). |
|
||||
| Time | Choose one of the following:<ul><li>**Send now** sends the report immediately after you save it. To stop sending the report at some point in the future, add an end date.</li><li>**Send later** schedules a report for a later date. When you select this option, the required **Start date**, **Start time**, and **Time zone** options are displayed.</li></ul> |
|
||||
| End date | If you leave this field empty, the report is sent out indefinitely. |
|
||||
| Send only from Monday to Friday | For reports that have an hourly or daily frequency, you can choose to send them only from Monday to Friday. |
|
||||
| Send on the last day of the month | When you schedule a report with a monthly frequency, and set the start date between the 29th and the 31st of the month, the report is only sent during the months that have those dates. If you want the report to be sent every month, select the **Send on the last day of the month** option. This way, the report is sent on the last day of every month regardless of how many days there are in the month. |
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
### 4. Share
|
||||
|
||||
At this step, enter information related to sharing the report:
|
||||
|
||||
<!-- vale Grafana.GoogleLyHyphens = NO -->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ----------- |
|
||||
| Report name (required) | The name of the report as you want it to appear in the **Reports** list. The report name also populates the email subject line. |
|
||||
| Recipients (required) | Enter the email addresses of the people or teams that you want to receive the report, separated by commas or semicolons. |
|
||||
| Reply-to email address | The address that appears in the **Reply to** field of the email. |
|
||||
| Message | The body of the message in the email with the report. |
|
||||
| Include a dashboard link | Include a links to the dashboards in the report email. |
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- vale Grafana.GoogleLyHyphens = YES -->
|
||||
|
||||
Click **Send test email** in the top-right corner of the screen to verify that the configuration works as expected and to verify that emails are working.
|
||||
You can choose to send this email to the recipients configured for the report, or to a different set of email addresses only used for testing.
|
||||
|
||||
### 5. Confirm
|
||||
|
||||
At this step, the confirmation page displays all the report settings.
|
||||
Review them and confirm that they're correct or click the provided **Edit** links for each section to make updates.
|
||||
|
||||
Then, click **Send now** or **Schedule send**.
|
||||
|
||||
You can also save the report as a draft or discard it. Discarding the report is irreversible.
|
||||
|
||||
## Send a report using the API
|
||||
|
||||
You can send reports programmatically with the [send report](ref:send-report) endpoint using the HTTP API.
|
||||
|
||||
## Manage reports
|
||||
|
||||
You can view and manage all your reports, and create new ones, on the **Reporting** page:
|
||||
On the **Reports** page, you can view and manage your existing reports or create new ones.
|
||||
|
||||
{{< figure src="/media/docs/grafana/dashboards/screenshot-reporting-page-v12.0.png" max-width="750px" alt="The Reporting page" >}}
|
||||
|
||||
Alternatively, from any dashboard you can view and manage any reports generated from that dashboard, as well as create a new report
|
||||
You can also navigate to the list of all reports from the dashboard-specific list:
|
||||
|
||||
{{< figure src="/media/docs/grafana/dashboards/screenshot-report-drawer-v12.0.png" max-width="750px" alt="The open Report schedule drawer with an existing report" >}}
|
||||

|
||||
|
||||
### Edit reports
|
||||
|
||||
To edit a report, follow these steps:
|
||||
|
||||
1. Do one of the following:
|
||||
|
||||
- In the main menu, click **Dashboards > Reporting**.
|
||||
- Navigate to the dashboard from which the report was generated and click **Share > Schedule report**.
|
||||
|
||||
1. In the main menu, click **Dashboards > Reporting**.
|
||||
1. Click the row of the report you want to update.
|
||||
1. Make the necessary changes.
|
||||
1. Click the **Edit report** button in the top-right hand corner or click the **Edit** link for a specific section to go to that one directly.
|
||||
1. When you've finished making changes, click **Confirm** at the top of the screen to go to the last step.
|
||||
1. Click **Update report**.
|
||||
1. Click the **x** at the top of the drawer to close it.
|
||||
|
||||
### Pause or resume reports
|
||||
### Pause or resume reports {#pause-a-report}
|
||||
|
||||
You can pause and resume sending reports from the report list view.
|
||||
To do this, follow these steps:
|
||||
|
||||
1. Do one of the following:
|
||||
|
||||
- In the main menu, click **Dashboards > Reporting**.
|
||||
- Navigate to the dashboard from which the report was generated and click **Share > Schedule report**.
|
||||
|
||||
1. In the main menu, click **Dashboards > Reporting**.
|
||||
1. On the row of the report you want to update, do one of the following:
|
||||
|
||||
- Click the pause icon - The report won't be sent according to its schedule until it's resumed.
|
||||
- Click the resume icon - The report resumes on its previous schedule.
|
||||
|
||||
You can also pause or resume a report from **Update report** drawer.
|
||||
|
||||
### Delete reports
|
||||
|
||||
To delete a report, follow these steps:
|
||||
|
||||
1. Do one of the following:
|
||||
|
||||
- In the main menu, click **Dashboards > Reporting**.
|
||||
- Navigate to the dashboard from which the report was generated and click **Share > Schedule report**.
|
||||
|
||||
1. In the main menu, click **Dashboards > Reporting**.
|
||||
1. On the row of the report you want to update, click the trash can icon.
|
||||
1. Click **Delete** to confirm.
|
||||
|
||||
You can also delete a report from **Update report** drawer.
|
||||
|
||||
Deleting a report is irreversible.
|
||||
|
||||
## Troubleshoot Reporting
|
||||
|
||||
@@ -21,19 +21,8 @@ refs:
|
||||
|
||||
# Reporting settings
|
||||
|
||||
You can configure organization-wide report settings and branding options in **Dashboards > Reporting**.
|
||||
These settings are applied to all the reports for the current organization.
|
||||
|
||||
To access the settings, go to **Dashboards > Reporting** and click the **Report settings** button.
|
||||
This opens the **Report template settings** drawer, where you can make changes.
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
|
||||
The redesigned reporting feature, including the report settings drawer, is currently in public preview. Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available. To use this feature, enable the `newShareReportDrawer` feature toggle in your Grafana configuration file or, for Grafana Cloud, contact Support.
|
||||
|
||||
{{< /admonition >}}
|
||||
|
||||
You can also navigate these settings from the **Schedule report** drawer that opens when you create a report directly from a dashboard.
|
||||
You can configure organization-wide report settings and branding options in **Dashboards > Reporting > Settings**.
|
||||
Settings are applied to all the reports for the current organization.
|
||||
|
||||
## Attachment settings
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ weight: 200
|
||||
|
||||
{{< admonition type="caution" >}}
|
||||
|
||||
Dashboard JSON schema v2 is an experimental feature. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `dashboardNewLayouts` feature toggle in Grafana to use this feature.
|
||||
Dashboard JSON schema v2 is an [experimental](https://grafana.com/docs/release-life-cycle/) feature. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. To get early access to this feature, request it through [this form](https://docs.google.com/forms/d/e/1FAIpQLSd73nQzuhzcHJOrLFK4ef_uMxHAQiPQh1-rsQUT2MRqbeMLpg/viewform?usp=dialog).
|
||||
|
||||
**Do not enable this feature in production environments as it may result in the irreversible loss of data.**
|
||||
|
||||
@@ -32,7 +32,8 @@ Observability as Code works with all versions of the JSON model, and it's fully
|
||||
|
||||
## Before you begin
|
||||
|
||||
Schema v2 is automatically enabled with the Dynamic Dashboards feature toggle, `dashboardNewLayouts`.
|
||||
Schema v2 is automatically enabled with the Dynamic Dashboards feature toggle.
|
||||
To get early access to this feature, request it through [this form](https://docs.google.com/forms/d/e/1FAIpQLSd73nQzuhzcHJOrLFK4ef_uMxHAQiPQh1-rsQUT2MRqbeMLpg/viewform?usp=dialog).
|
||||
It also requires the new dashboards API feature toggle, `kubernetesDashboards`, to be enabled as well.
|
||||
|
||||
For more information on how dashboards behave depending on your feature flag configuration, refer to [Notes and limitations](#notes-and-limitations).
|
||||
@@ -217,18 +218,18 @@ Upon saving, they’ll be updated to the new schema where you can take advantage
|
||||
|
||||
### Dashboard behavior with disabled feature flags
|
||||
|
||||
If you disable the `dashboardNewLayouts` or `kubernetesDashboards` feature flags, you should be aware of how dashboards will behave.
|
||||
If you disable the Dynamic dashboards or `kubernetesDashboards` feature flags, you should be aware of how dashboards will behave.
|
||||
|
||||
#### Disable `dashboardNewLayouts`
|
||||
#### Disable Dynamic dashboards
|
||||
|
||||
If `dashboardNewLayouts` feature toggle is disabled, depending on how the dashboard was built, it will behave differently:
|
||||
If the Dynamic dashboards feature toggle is disabled, depending on how the dashboard was built, it will behave differently:
|
||||
|
||||
- Dashboards built on the new schema through the UI - View only
|
||||
- Dashboards built on Schema v1 - View and edit
|
||||
- Dashboards built on the new schema by way of Terraform or the CLI - View and edit
|
||||
- Provisioned dashboards built on the new schema - View and edit, but the edit experience will be the old experience
|
||||
|
||||
#### Disable `dashboardNewLayouts` and `kubernetesDashboards`
|
||||
#### Disable Dynamic dashboards and `kubernetesDashboards`
|
||||
|
||||
You’ll be unable to view or edit dashboards created or updated in the new schema.
|
||||
|
||||
|
||||
@@ -2838,22 +2838,14 @@ For example:
|
||||
|
||||
```ini
|
||||
[time_picker]
|
||||
quick_ranges = [
|
||||
{
|
||||
"display": "Last 5 minutes",
|
||||
"from": "now-5m",
|
||||
"to": "now",
|
||||
},
|
||||
{
|
||||
"display": "Yesterday",
|
||||
"from": "now-1d/d",
|
||||
},
|
||||
{
|
||||
"display": "Today so far",
|
||||
"from": "now/d",
|
||||
"to": "now",
|
||||
}
|
||||
]
|
||||
quick_ranges = """[
|
||||
{"from":"now-6s","to":"now","display":"Last 6 seconds"},
|
||||
{"from":"now-10m","to":"now","display":"Last 10 minutes"},
|
||||
{"from":"now-25h","to":"now","display":"Last 24 hours"},
|
||||
{"from":"now/w","to":"now/w","display":"This week"},
|
||||
{"from":"now-1w/w","to":"now-1w/w","display":"Last week"},
|
||||
{"from":"now-10d","to":"now","display":"Last 10 days"}
|
||||
]"""
|
||||
```
|
||||
|
||||
### `[expressions]`
|
||||
|
||||
@@ -20,7 +20,8 @@ weight: 300
|
||||
System for Cross-domain Identity Management (SCIM) is an open standard that allows automated user provisioning and management. With SCIM, you can automate the provisioning of users and groups from your identity provider to Grafana.
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
Available in [Grafana Enterprise](/docs/grafana/<GRAFANA_VERSION>/introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/).
|
||||
Available in [Grafana Enterprise](/docs/grafana/<GRAFANA_VERSION>/introduction/grafana-enterprise/) and [Grafana Cloud Pro and Advanced](/docs/grafana-cloud/) in [public preview](https://grafana.com/docs/release-life-cycle/).
|
||||
Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available.
|
||||
{{< /admonition >}}
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
|
||||
@@ -26,6 +26,7 @@ For each action, define the following API call settings:
|
||||
| Confirmation message | A descriptive prompt to confirm or cancel the action. |
|
||||
| Method | Select from **POST**, **PUT**, or **GET**. |
|
||||
| URL | The request URL.</p><p>To add a variable, click in the **URL** field and enter `$` or press Ctrl+Space or Cmd+Space to see a list of available variables. |
|
||||
| Variables | **Key** and **Name** pairs with a type selection. Click the **+** icon to add as many variables as you need. To add a variable to the request, prefix the key with `$`. You can set the values for the variables when performing an action. |
|
||||
| Query parameters | **Key** and **Value** pairs. Click the **+** icon to add as many key/value pairs as you need. |
|
||||
| Headers | Comprised of **Key** and **Value** pairs and a **Content-Type**.</p><p>Click the **+** icon to add as many key/value pairs as you need. |
|
||||
| Content-Type | Select from the following: **application/json**, **text/plain**, **application/XML**, and **application/x-www-form-urlencoded**. |
|
||||
|
||||
@@ -28,6 +28,7 @@ For each action, define the following API call settings:
|
||||
| Confirmation message | A descriptive prompt to confirm or cancel the action. |
|
||||
| Method | Select from **POST**, **PUT**, or **GET**. |
|
||||
| URL | The request URL.</p><p>To add a variable, click in the **URL** field and enter `$` or press Ctrl+Space or Cmd+Space to see a list of available variables. |
|
||||
| Variables | **Key** and **Name** pairs with a type selection. Click the **+** icon to add as many variables as you need. To add a variable to the request, prefix the key with `$`. You can set the values for the variables when performing an action. |
|
||||
| Query parameters | **Key** and **Value** pairs. Click the **+** icon to add as many key/value pairs as you need. |
|
||||
| Headers | Comprised of **Key** and **Value** pairs and a **Content-Type**.</p><p>Click the **+** icon to add as many key/value pairs as you need. |
|
||||
| Content-Type | Select from the following: **application/json**, **text/plain**, **application/XML**, and **application/x-www-form-urlencoded**. |
|
||||
|
||||
@@ -96,27 +96,24 @@ For self-managed Grafana (both Enterprise and OSS), the support for versions fol
|
||||
- Each minor release is supported for 9 months after its release date
|
||||
- The last minor release of a major version receives extended support for 15 months after its release date
|
||||
- Support levels change as new versions are released:
|
||||
- **Full Support**: The most recently released major/minor (and the last minor of the previous major) version receive full support including new features, bug fixes, and security patches
|
||||
- **Security & Critical Bugs Only**: Versions that are not the most recently released major/minor (or the last minor of the previous major) version, but still within their support period, receive only security patches and critical bug fixes
|
||||
- **Full Support**: The most recently released major/minor version receive full support including new features, bug fixes, and security patches
|
||||
- **Security & Critical Bugs Only**: Versions that are outside of the most recently released major/minor version, but still within their support period, receive only security patches and critical bug fixes
|
||||
- **Not Supported**: Versions beyond their support period receive no updates
|
||||
|
||||
Here is an overview of version support through 2026:
|
||||
|
||||
| **Version** | **Release date** | **Support end date** | **Support level** |
|
||||
| ------------------------- | ------------------ | -------------------- | ----------------------------- |
|
||||
| 9.5.x (Last minor of 9) | April 26, 2023 | July 26, 2024 | Supported for Azure Only |
|
||||
| 10.0.x | June 13, 2023 | March 13, 2024 | Not Supported |
|
||||
| 10.1.x | August 22, 2023 | May 22, 2024 | Not Supported |
|
||||
| 10.2.x | October 24, 2023 | July 24, 2024 | Not Supported |
|
||||
| 10.3.x | January 23, 2024 | October 23, 2024 | Not Supported |
|
||||
| 10.4.x (Last minor of 10) | March 5, 2024 | June 5, 2025 | Security & Critical Bugs Only |
|
||||
| 11.0.x | May 14, 2024 | February 14, 2025 | Security & Critical Bugs Only |
|
||||
| 11.1.x | June 25, 2024 | April 23, 2025 | Security & Critical Bugs Only |
|
||||
| 11.2.x | August 27, 2024 | May 27, 2025 | Security & Critical Bugs Only |
|
||||
| 10.4.x (Last minor of 10) | March 5, 2024 | June 5, 2025 | Not Supported |
|
||||
| 11.0.x | May 14, 2024 | February 14, 2025 | Not Supported |
|
||||
| 11.1.x | June 25, 2024 | April 23, 2025 | Not Supported |
|
||||
| 11.2.x | August 27, 2024 | May 27, 2025 | Not Supported |
|
||||
| 11.3.x | October 22, 2024 | July 22, 2025 | Security & Critical Bugs Only |
|
||||
| 11.4.x | December 5, 2024 | September 5, 2025 | Security & Critical Bugs Only |
|
||||
| 11.5.x | January 28, 2025 | October 28, 2025 | Security & Critical Bugs Only |
|
||||
| 11.6.x (Last minor of 11) | March 25, 2025 | May 25, 2026 | Full Support |
|
||||
| 11.6.x (Last minor of 11) | March 25, 2025 | May 25, 2026 | Security & Critical Bugs Only |
|
||||
| 12.0.x | May 5, 2025 | February 5, 2026 | Full Support until next minor |
|
||||
| 12.1.x | July 22, 2025 | April 22, 2026 | Full Support until next minor |
|
||||
| 12.2.x | September 23, 2025 | June 23, 2026 | Full Support until next minor |
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
2
go.mod
2
go.mod
@@ -99,7 +99,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
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1610,8 +1610,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=
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
@@ -39,6 +41,9 @@ var getViewIndex = func() string {
|
||||
return viewIndex
|
||||
}
|
||||
|
||||
// Only allow redirects that start with an alphanumerical character, a dash or an underscore.
|
||||
var redirectRe = regexp.MustCompile(`^/[a-zA-Z0-9-_].*`)
|
||||
|
||||
var (
|
||||
errAbsoluteRedirectTo = errors.New("absolute URLs are not allowed for redirect_to cookie value")
|
||||
errInvalidRedirectTo = errors.New("invalid redirect_to cookie value")
|
||||
@@ -68,6 +73,15 @@ func (hs *HTTPServer) ValidateRedirectTo(redirectTo string) error {
|
||||
return errForbiddenRedirectTo
|
||||
}
|
||||
|
||||
cleanPath := path.Clean(to.Path)
|
||||
// "." is what path.Clean returns for empty paths
|
||||
if cleanPath == "." {
|
||||
return errForbiddenRedirectTo
|
||||
}
|
||||
if to.Path != "/" && !redirectRe.MatchString(cleanPath) {
|
||||
return errForbiddenRedirectTo
|
||||
}
|
||||
|
||||
// when using a subUrl, the redirect_to should start with the subUrl (which contains the leading slash), otherwise the redirect
|
||||
// will send the user to the wrong location
|
||||
if hs.Cfg.AppSubURL != "" && !strings.HasPrefix(to.Path, hs.Cfg.AppSubURL+"/") {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/user/usertest"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/web/webtest"
|
||||
)
|
||||
|
||||
func TestUserTokenAPIEndpoint(t *testing.T) {
|
||||
@@ -150,6 +152,95 @@ func TestUserTokenAPIEndpoint(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTPServer_RotateUserAuthTokenRedirect(t *testing.T) {
|
||||
redirectTestCases := []struct {
|
||||
name string
|
||||
redirectUrl string
|
||||
expectedUrl string
|
||||
}{
|
||||
// Valid redirects should be preserved
|
||||
{"valid root path", "/", "/"},
|
||||
{"valid simple path", "/hello", "/hello"},
|
||||
{"valid single char path", "/a", "/a"},
|
||||
{"valid nested path", "/asd/hello", "/asd/hello"},
|
||||
|
||||
// Invalid redirects should be converted to root
|
||||
{"backslash domain", `/\grafana.com`, "/"},
|
||||
{"traversal backslash domain", `/a/../\grafana.com`, "/"},
|
||||
{"double slash", "//grafana", "/"},
|
||||
{"missing initial slash", "missingInitialSlash", "/"},
|
||||
{"parent directory", "/../", "/"},
|
||||
}
|
||||
|
||||
sessionTestCases := []struct {
|
||||
name string
|
||||
useSessionStorageRedirect bool
|
||||
}{
|
||||
{"when useSessionStorageRedirect is enabled", true},
|
||||
{"when useSessionStorageRedirect is disabled", false},
|
||||
}
|
||||
|
||||
for _, sessionCase := range sessionTestCases {
|
||||
t.Run(sessionCase.name, func(t *testing.T) {
|
||||
for _, redirectCase := range redirectTestCases {
|
||||
t.Run(redirectCase.name, func(t *testing.T) {
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.LoginCookieName = "grafana_session"
|
||||
cfg.LoginMaxLifetime = 10 * time.Hour
|
||||
hs.Cfg = cfg
|
||||
hs.log = log.New()
|
||||
hs.AuthTokenService = &authtest.FakeUserAuthTokenService{
|
||||
RotateTokenProvider: func(ctx context.Context, cmd auth.RotateCommand) (*auth.UserToken, error) {
|
||||
return &auth.UserToken{UnhashedToken: "new"}, nil
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
redirectToQuery := url.QueryEscape(redirectCase.redirectUrl)
|
||||
urlString := "/user/auth-tokens/rotate"
|
||||
|
||||
if sessionCase.useSessionStorageRedirect {
|
||||
urlString = urlString + "?redirectTo=" + redirectToQuery
|
||||
}
|
||||
|
||||
req := server.NewGetRequest(urlString)
|
||||
req.AddCookie(&http.Cookie{Name: "grafana_session", Value: "123", Path: "/"})
|
||||
|
||||
if sessionCase.useSessionStorageRedirect {
|
||||
req = webtest.RequestWithWebContext(req, &contextmodel.ReqContext{UseSessionStorageRedirect: true})
|
||||
} else {
|
||||
req.AddCookie(&http.Cookie{Name: "redirect_to", Value: redirectToQuery, Path: "/"})
|
||||
}
|
||||
|
||||
var redirectStatusCode int
|
||||
var redirectLocation string
|
||||
|
||||
server.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
if len(via) > 1 {
|
||||
// Stop after first redirect
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
if req.Response == nil {
|
||||
return nil
|
||||
}
|
||||
redirectStatusCode = req.Response.StatusCode
|
||||
redirectLocation = req.Response.Header.Get("Location")
|
||||
return nil
|
||||
}
|
||||
res, err := server.Send(req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 302, redirectStatusCode)
|
||||
assert.Equal(t, redirectCase.expectedUrl, redirectLocation)
|
||||
|
||||
require.NoError(t, res.Body.Close())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPServer_RotateUserAuthToken(t *testing.T) {
|
||||
type testCase struct {
|
||||
desc string
|
||||
|
||||
@@ -3,6 +3,8 @@ package middleware
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
||||
@@ -11,6 +13,9 @@ import (
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
// Only allow redirects that start with an alphanumerical character, a dash or an underscore.
|
||||
var redirectRe = regexp.MustCompile(`^/?[a-zA-Z0-9-_].*`)
|
||||
|
||||
// OrgRedirect changes org and redirects users if the
|
||||
// querystring `orgId` doesn't match the active org.
|
||||
func OrgRedirect(cfg *setting.Cfg, userSvc user.Service) web.Handler {
|
||||
@@ -31,6 +36,11 @@ func OrgRedirect(cfg *setting.Cfg, userSvc user.Service) web.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
if !validRedirectPath(c.Req.URL.Path) {
|
||||
// Do not switch orgs or perform the redirect because the new path is not valid
|
||||
return
|
||||
}
|
||||
|
||||
if err := userSvc.Update(ctx.Req.Context(), &user.UpdateUserCommand{UserID: ctx.UserID, OrgID: &orgId}); err != nil {
|
||||
if ctx.IsApiRequest() {
|
||||
ctx.JsonApiErr(404, "Not found", nil)
|
||||
@@ -54,3 +64,8 @@ func OrgRedirect(cfg *setting.Cfg, userSvc user.Service) web.Handler {
|
||||
c.Redirect(newURL, 302)
|
||||
}
|
||||
}
|
||||
|
||||
func validRedirectPath(p string) bool {
|
||||
cleanPath := path.Clean(p)
|
||||
return cleanPath == "." || cleanPath == "/" || redirectRe.MatchString(cleanPath)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -23,6 +24,12 @@ func TestOrgRedirectMiddleware(t *testing.T) {
|
||||
expStatus: 302,
|
||||
expLocation: "/?orgId=3",
|
||||
},
|
||||
{
|
||||
desc: "when setting a correct org for the user with an empty path",
|
||||
input: "?orgId=3",
|
||||
expStatus: 302,
|
||||
expLocation: "/?orgId=3",
|
||||
},
|
||||
{
|
||||
desc: "when setting a correct org for the user with '&kiosk'",
|
||||
input: "/?orgId=3&kiosk",
|
||||
@@ -64,6 +71,16 @@ func TestOrgRedirectMiddleware(t *testing.T) {
|
||||
require.Equal(t, 404, sc.resp.Code)
|
||||
})
|
||||
|
||||
middlewareScenario(t, "when redirecting to an invalid path", func(t *testing.T, sc *scenarioContext) {
|
||||
sc.withIdentity(&authn.Identity{})
|
||||
|
||||
path := url.QueryEscape(`/\example.com`)
|
||||
sc.m.Get(url.QueryEscape(path), sc.defaultHandler)
|
||||
sc.fakeReq("GET", fmt.Sprintf("%s?orgId=3", path)).exec()
|
||||
|
||||
require.Equal(t, 404, sc.resp.Code)
|
||||
})
|
||||
|
||||
middlewareScenario(t, "works correctly when grafana is served under a subpath", func(t *testing.T, sc *scenarioContext) {
|
||||
sc.withIdentity(&authn.Identity{})
|
||||
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -2923,11 +2923,16 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"title",
|
||||
"description",
|
||||
"version",
|
||||
"group",
|
||||
"permissions"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"group": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
@@ -3236,11 +3241,16 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"title",
|
||||
"description",
|
||||
"version",
|
||||
"group",
|
||||
"permissions"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"group": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
@@ -3723,11 +3733,16 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"title",
|
||||
"description",
|
||||
"version",
|
||||
"group",
|
||||
"permissions"
|
||||
],
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"group": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
|
||||
@@ -147,6 +147,7 @@ func requestContextMiddleware() web.Middleware {
|
||||
c.RequestNonce = ctx.RequestNonce
|
||||
c.PerfmonTimer = ctx.PerfmonTimer
|
||||
c.LookupTokenErr = ctx.LookupTokenErr
|
||||
c.UseSessionStorageRedirect = ctx.UseSessionStorageRedirect
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
|
||||
@@ -2,7 +2,7 @@ import { uniqueId } from 'lodash';
|
||||
import { HttpResponse, http } from 'msw';
|
||||
import { Route, Routes } from 'react-router-dom-v5-compat';
|
||||
import { Props } from 'react-virtualized-auto-sizer';
|
||||
import { render, screen, waitFor, waitForElementToBeRemoved, within } from 'test/test-utils';
|
||||
import { render, screen, waitFor, within } from 'test/test-utils';
|
||||
import { byRole, byTestId } from 'testing-library-selector';
|
||||
|
||||
import { setPluginLinksHook } from '@grafana/runtime';
|
||||
@@ -41,7 +41,6 @@ const ui = {
|
||||
header: byRole('heading', { level: 1 }),
|
||||
editLink: byRole('link', { name: 'Edit' }),
|
||||
exportButton: byRole('button', { name: 'Export' }),
|
||||
ruleLoader: byTestId('alert-rule-list-item-loader'),
|
||||
ruleItem: byRole('treeitem'),
|
||||
export: {
|
||||
dialog: byRole('dialog', { name: /Drawer title Export .* rules/ }),
|
||||
@@ -161,7 +160,7 @@ describe('GroupDetailsPage', () => {
|
||||
'/alerting/grafana/namespaces/test-folder-uid/groups/test-group-cpu/edit?returnTo=%2Falerting%2Fgrafana%2Fnamespaces%2Ftest-folder-uid%2Fgroups%2Ftest-group-cpu%2Fview'
|
||||
);
|
||||
|
||||
await waitForElementToBeRemoved(() => ui.ruleLoader.queryAll());
|
||||
expect(await screen.findByRole('treeitem', { name: rule1.grafana_alert.title })).toBeInTheDocument();
|
||||
|
||||
const alertRuleItems = await ui.ruleItem.findAll();
|
||||
expect(alertRuleItems).toHaveLength(2);
|
||||
@@ -221,11 +220,9 @@ describe('GroupDetailsPage', () => {
|
||||
// Act
|
||||
renderGroupDetailsPage('grafana', 'test-folder-uid', group.name);
|
||||
|
||||
// wait for loaders to show and dissapear
|
||||
await waitFor(() => expect(ui.ruleLoader.queryAll()).toHaveLength(3));
|
||||
await waitForElementToBeRemoved(() => ui.ruleLoader.queryAll());
|
||||
|
||||
const alertRuleItems = await ui.ruleItem.findAll();
|
||||
// Wait until rule items are rendered
|
||||
expect(await screen.findByRole('treeitem', { name: rule1.grafana_alert.title })).toBeInTheDocument();
|
||||
const alertRuleItems = ui.ruleItem.getAll();
|
||||
|
||||
// Assert
|
||||
expect(alertRuleItems).toHaveLength(2);
|
||||
@@ -238,9 +235,8 @@ describe('GroupDetailsPage', () => {
|
||||
// Act
|
||||
renderGroupDetailsPage('grafana', 'test-folder-uid', group.name);
|
||||
|
||||
// wait for loaders to show and dissapear
|
||||
await waitFor(() => expect(ui.ruleLoader.queryAll()).toHaveLength(3));
|
||||
await waitForElementToBeRemoved(() => ui.ruleLoader.queryAll());
|
||||
// wait for rule items to render
|
||||
expect(await screen.findByRole('treeitem', { name: rule1.grafana_alert.title })).toBeInTheDocument();
|
||||
|
||||
const alertRuleItems = await ui.ruleItem.findAll();
|
||||
|
||||
@@ -264,9 +260,8 @@ describe('GroupDetailsPage', () => {
|
||||
|
||||
// Act
|
||||
renderGroupDetailsPage('grafana', 'test-folder-uid', provisionedGroup.name);
|
||||
// wait for loaders to show and dissapear
|
||||
await waitFor(() => expect(ui.ruleLoader.queryAll()).toHaveLength(3));
|
||||
await waitForElementToBeRemoved(() => ui.ruleLoader.queryAll());
|
||||
// wait for rule items to render
|
||||
expect(await screen.findByRole('treeitem', { name: provisionedRule.grafana_alert.title })).toBeInTheDocument();
|
||||
|
||||
const alertRuleItems = await ui.ruleItem.findAll();
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export function AlertRuleListItemSkeleton() {
|
||||
description={<Skeleton width={256} />}
|
||||
actions={<RuleActionsSkeleton />}
|
||||
data-testid="alert-rule-list-item-loader"
|
||||
aria-disabled={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,39 +37,6 @@ jest.mock('@grafana/runtime', () => {
|
||||
...original.config.featureToggles,
|
||||
dashboardNewLayouts: false, // Default value
|
||||
},
|
||||
|
||||
bootData: {
|
||||
...original.config.bootData,
|
||||
settings: {
|
||||
...original.config.bootData.settings,
|
||||
datasources: {
|
||||
'gdev-testdata': {
|
||||
id: 7,
|
||||
uid: 'abc',
|
||||
type: 'grafana-testdata-datasource',
|
||||
name: 'gdev-testdata',
|
||||
meta: {
|
||||
id: 'grafana-testdata-datasource',
|
||||
type: 'datasource',
|
||||
name: 'TestData',
|
||||
aliasIDs: ['testdata'],
|
||||
},
|
||||
},
|
||||
'-- Grafana --': {
|
||||
id: -1,
|
||||
uid: 'grafana',
|
||||
type: 'datasource',
|
||||
name: '-- Grafana --',
|
||||
meta: {
|
||||
id: 'grafana',
|
||||
type: 'datasource',
|
||||
name: '-- Grafana --',
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultDatasource: 'gdev-testdata',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -1621,11 +1588,9 @@ const v2ProvisionedDashboardResource = {
|
||||
kind: 'AnnotationQuery',
|
||||
spec: {
|
||||
builtIn: true,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
group: 'grafana',
|
||||
spec: {},
|
||||
version: 'v0',
|
||||
datasource: {
|
||||
type: 'grafana',
|
||||
uid: '-- Grafana --',
|
||||
},
|
||||
enable: true,
|
||||
hide: true,
|
||||
@@ -1648,14 +1613,13 @@ const v2ProvisionedDashboardResource = {
|
||||
{
|
||||
kind: 'PanelQuery',
|
||||
spec: {
|
||||
datasource: {
|
||||
type: 'grafana-testdata-datasource',
|
||||
uid: 'PD8C576611E62080A',
|
||||
},
|
||||
hidden: false,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
group: 'grafana-testdata-datasource',
|
||||
version: 'v0',
|
||||
datasource: {
|
||||
name: 'PD8C576611E62080A',
|
||||
},
|
||||
kind: 'grafana-testdata-datasource',
|
||||
spec: {
|
||||
scenarioId: 'random_walk',
|
||||
seriesCount: 2,
|
||||
|
||||
@@ -549,12 +549,13 @@ describe('dashboard exporter v2', () => {
|
||||
{
|
||||
kind: 'PanelQuery',
|
||||
spec: {
|
||||
datasource: {
|
||||
type: 'prometheus',
|
||||
uid: '${datasourceVar}',
|
||||
},
|
||||
hidden: false,
|
||||
query: {
|
||||
datasource: {
|
||||
name: '${datasourceVar}',
|
||||
},
|
||||
group: 'prometheus',
|
||||
kind: 'prometheus',
|
||||
spec: {
|
||||
editorMode: 'builder',
|
||||
expr: 'go_goroutines{job="prometheus"}',
|
||||
@@ -582,7 +583,7 @@ describe('dashboard exporter v2', () => {
|
||||
it('should replace datasource in a query variable', async () => {
|
||||
const { dashboard } = await setup();
|
||||
const variable = dashboard.variables[0] as QueryVariableKind;
|
||||
expect(variable.spec.query.datasource?.name).toBeUndefined();
|
||||
expect(variable.spec.datasource?.uid).toBeUndefined();
|
||||
});
|
||||
|
||||
it('do not expose datasource name and id in datasource variable', async () => {
|
||||
@@ -596,7 +597,7 @@ describe('dashboard exporter v2', () => {
|
||||
const { dashboard } = await setup();
|
||||
const annotationQuery = dashboard.annotations[0];
|
||||
|
||||
expect(annotationQuery.spec.query?.datasource?.name).toBeUndefined();
|
||||
expect(annotationQuery.spec.datasource?.uid).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should remove library panels from layout', async () => {
|
||||
@@ -615,8 +616,11 @@ describe('dashboard exporter v2', () => {
|
||||
if (panel.kind !== 'Panel') {
|
||||
throw new Error('Panel should be a Panel');
|
||||
}
|
||||
expect(panel.spec.data.spec.queries[0].spec.query.datasource?.name).toBe('${datasourceVar}');
|
||||
expect(panel.spec.data.spec.queries[0].spec.query.group).toBe('prometheus');
|
||||
|
||||
expect(panel.spec.data.spec.queries[0].spec.datasource).toEqual({
|
||||
type: 'prometheus',
|
||||
uid: '${datasourceVar}',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -344,8 +344,7 @@ export async function makeExportableV2(dashboard: DashboardV2Spec) {
|
||||
const removeDataSourceRefs = (
|
||||
obj: AnnotationQueryKind['spec'] | QueryVariableKind['spec'] | PanelQueryKind['spec']
|
||||
) => {
|
||||
const datasourceUid = obj.query?.datasource?.name;
|
||||
|
||||
const datasourceUid = obj.datasource?.uid;
|
||||
if (datasourceUid?.startsWith('${') && datasourceUid?.endsWith('}')) {
|
||||
const varName = datasourceUid.slice(2, -1);
|
||||
// if there's a match we don't want to remove the datasource ref
|
||||
@@ -355,7 +354,7 @@ export async function makeExportableV2(dashboard: DashboardV2Spec) {
|
||||
}
|
||||
}
|
||||
|
||||
obj.query && (obj.query.datasource = undefined);
|
||||
obj.datasource = undefined;
|
||||
};
|
||||
|
||||
const processPanel = (panel: PanelKind) => {
|
||||
|
||||
@@ -10,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -45,8 +45,8 @@ const getDataSourceMock = jest.fn();
|
||||
|
||||
const fakeDsMock: DataSourceApi = {
|
||||
name: 'fake-std',
|
||||
type: 'fake-type',
|
||||
getRef: () => ({ type: 'fake-type', uid: 'fake-uid' }),
|
||||
type: 'fake-std',
|
||||
getRef: () => ({ type: 'fake-std', uid: 'fake-std' }),
|
||||
query: () =>
|
||||
Promise.resolve({
|
||||
data: [],
|
||||
@@ -74,7 +74,7 @@ const fakeDsMock: DataSourceApi = {
|
||||
toDataQuery: (q) => ({ ...q, refId: 'FakeDataSource-refId' }),
|
||||
},
|
||||
id: 1,
|
||||
uid: 'fake-uid',
|
||||
uid: 'fake-std',
|
||||
};
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
@@ -95,7 +95,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
description: 'test-desc',
|
||||
value: ['selected-value'],
|
||||
text: ['selected-value-text'],
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
query: 'query',
|
||||
includeAll: true,
|
||||
allowCustomValue: true,
|
||||
@@ -123,8 +123,8 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
],
|
||||
},
|
||||
"datasource": {
|
||||
"type": "fake-type",
|
||||
"uid": "fake-uid",
|
||||
"type": "fake-std",
|
||||
"uid": "fake-std",
|
||||
},
|
||||
"definition": undefined,
|
||||
"description": "test-desc",
|
||||
@@ -148,7 +148,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
description: 'test-desc',
|
||||
value: ['selected-value'],
|
||||
text: ['selected-value-text'],
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
query: 'query',
|
||||
definition: 'query',
|
||||
includeAll: true,
|
||||
@@ -176,8 +176,8 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
],
|
||||
},
|
||||
"datasource": {
|
||||
"type": "fake-type",
|
||||
"uid": "fake-uid",
|
||||
"type": "fake-std",
|
||||
"uid": "fake-std",
|
||||
},
|
||||
"definition": "query",
|
||||
"description": "test-desc",
|
||||
@@ -201,7 +201,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
description: 'test-desc',
|
||||
value: ['selected-value'],
|
||||
text: ['selected-value-text'],
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
query: 'query',
|
||||
options: [
|
||||
{ label: 'test', value: 'test' },
|
||||
@@ -228,7 +228,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
description: 'test-desc',
|
||||
value: ['test'],
|
||||
text: ['test'],
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
query: 'query',
|
||||
options: [
|
||||
{ label: 'test', value: 'test' },
|
||||
@@ -499,7 +499,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
allowCustomValue: true,
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
filters: [
|
||||
{
|
||||
key: 'filterTest',
|
||||
@@ -533,8 +533,8 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
},
|
||||
],
|
||||
"datasource": {
|
||||
"type": "fake-type",
|
||||
"uid": "fake-uid",
|
||||
"type": "fake-std",
|
||||
"uid": "fake-std",
|
||||
},
|
||||
"defaultKeys": undefined,
|
||||
"description": "test-desc",
|
||||
@@ -662,7 +662,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
allowCustomValue: true,
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
defaultKeys: [
|
||||
{
|
||||
text: 'some',
|
||||
@@ -710,8 +710,8 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
},
|
||||
],
|
||||
"datasource": {
|
||||
"type": "fake-type",
|
||||
"uid": "fake-uid",
|
||||
"type": "fake-std",
|
||||
"uid": "fake-std",
|
||||
},
|
||||
"defaultKeys": [
|
||||
{
|
||||
@@ -757,7 +757,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
allowCustomValue: true,
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
defaultOptions: [
|
||||
{
|
||||
text: 'Foo',
|
||||
@@ -784,8 +784,8 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
"value": [],
|
||||
},
|
||||
"datasource": {
|
||||
"type": "fake-type",
|
||||
"uid": "fake-uid",
|
||||
"type": "fake-std",
|
||||
"uid": "fake-std",
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": "test-desc",
|
||||
@@ -813,7 +813,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
defaultOptions: [
|
||||
{
|
||||
text: 'Foo',
|
||||
@@ -841,7 +841,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
description: 'test-desc',
|
||||
value: ['selected-value'],
|
||||
text: ['selected-value-text'],
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
query: 'query',
|
||||
includeAll: true,
|
||||
allValue: 'test-all',
|
||||
@@ -856,45 +856,44 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchInlineSnapshot(`
|
||||
{
|
||||
"kind": "QueryVariable",
|
||||
{
|
||||
"kind": "QueryVariable",
|
||||
"spec": {
|
||||
"allValue": "test-all",
|
||||
"allowCustomValue": true,
|
||||
"current": {
|
||||
"text": [
|
||||
"selected-value-text",
|
||||
],
|
||||
"value": [
|
||||
"selected-value",
|
||||
],
|
||||
},
|
||||
"datasource": {
|
||||
"type": "fake-std",
|
||||
"uid": "fake-std",
|
||||
},
|
||||
"definition": undefined,
|
||||
"description": "test-desc",
|
||||
"hide": "dontHide",
|
||||
"includeAll": true,
|
||||
"label": "test-label",
|
||||
"multi": true,
|
||||
"name": "test",
|
||||
"options": [],
|
||||
"query": {
|
||||
"kind": "fake-std",
|
||||
"spec": {
|
||||
"allValue": "test-all",
|
||||
"allowCustomValue": true,
|
||||
"current": {
|
||||
"text": [
|
||||
"selected-value-text",
|
||||
],
|
||||
"value": [
|
||||
"selected-value",
|
||||
],
|
||||
},
|
||||
"definition": undefined,
|
||||
"description": "test-desc",
|
||||
"hide": "dontHide",
|
||||
"includeAll": true,
|
||||
"label": "test-label",
|
||||
"multi": true,
|
||||
"name": "test",
|
||||
"options": [],
|
||||
"query": {
|
||||
"datasource": {
|
||||
"name": "fake-uid",
|
||||
},
|
||||
"group": "fake-type",
|
||||
"kind": "DataQuery",
|
||||
"spec": {
|
||||
"__legacyStringValue": "query",
|
||||
},
|
||||
"version": "v0",
|
||||
},
|
||||
"refresh": "onDashboardLoad",
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": "disabled",
|
||||
"__legacyStringValue": "query",
|
||||
},
|
||||
}
|
||||
`);
|
||||
},
|
||||
"refresh": "onDashboardLoad",
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": "disabled",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should handle CustomVariable', () => {
|
||||
@@ -1153,7 +1152,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
filters: [
|
||||
{
|
||||
key: 'filterTest',
|
||||
@@ -1189,8 +1188,8 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
},
|
||||
],
|
||||
"datasource": {
|
||||
"type": "fake-type",
|
||||
"uid": "fake-uid",
|
||||
"type": "fake-std",
|
||||
"uid": "fake-std",
|
||||
},
|
||||
"defaultKeys": [],
|
||||
"description": "test-desc",
|
||||
@@ -1215,7 +1214,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
defaultKeys: [
|
||||
{
|
||||
text: 'some',
|
||||
@@ -1265,8 +1264,8 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
},
|
||||
],
|
||||
"datasource": {
|
||||
"type": "fake-type",
|
||||
"uid": "fake-uid",
|
||||
"type": "fake-std",
|
||||
"uid": "fake-std",
|
||||
},
|
||||
"defaultKeys": [
|
||||
{
|
||||
@@ -1313,7 +1312,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
defaultOptions: [
|
||||
{
|
||||
text: 'Foo',
|
||||
@@ -1341,8 +1340,8 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
"value": [],
|
||||
},
|
||||
"datasource": {
|
||||
"type": "fake-type",
|
||||
"uid": "fake-uid",
|
||||
"type": "fake-std",
|
||||
"uid": "fake-std",
|
||||
},
|
||||
"defaultValue": undefined,
|
||||
"description": "test-desc",
|
||||
@@ -1373,7 +1372,7 @@ describe('sceneVariablesSetToVariables', () => {
|
||||
name: 'test',
|
||||
label: 'test-label',
|
||||
description: 'test-desc',
|
||||
datasource: { uid: 'fake-uid', type: 'fake-type' },
|
||||
datasource: { uid: 'fake-std', type: 'fake-std' },
|
||||
defaultOptions: [
|
||||
{
|
||||
text: 'Foo',
|
||||
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
GroupByVariableKind,
|
||||
defaultVariableHide,
|
||||
VariableOption,
|
||||
defaultDataQueryKind,
|
||||
AdHocFilterWithLabels,
|
||||
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha1/types.spec.gen';
|
||||
|
||||
@@ -295,30 +294,14 @@ export function sceneVariablesSetToSchemaV2Variables(
|
||||
}
|
||||
const query = variable.state.query;
|
||||
let dataQuery: DataQueryKind | string;
|
||||
const datasource = getElementDatasource(set, variable, 'variable', undefined, dsReferencesMapping);
|
||||
|
||||
if (typeof query !== 'string') {
|
||||
dataQuery = {
|
||||
kind: 'DataQuery',
|
||||
version: defaultDataQueryKind().version,
|
||||
group: datasource?.type ?? getDataQueryKind(query),
|
||||
...(datasource?.uid && {
|
||||
datasource: {
|
||||
name: datasource.uid,
|
||||
},
|
||||
}),
|
||||
kind: variable.state.datasource?.type ?? getDataQueryKind(query),
|
||||
spec: getDataQuerySpec(query),
|
||||
};
|
||||
} else {
|
||||
dataQuery = {
|
||||
kind: 'DataQuery',
|
||||
version: defaultDataQueryKind().version,
|
||||
group: datasource?.type ?? getDataQueryKind(query),
|
||||
...(datasource?.uid && {
|
||||
datasource: {
|
||||
name: datasource.uid,
|
||||
},
|
||||
}),
|
||||
kind: variable.state.datasource?.type ?? getDataQueryKind(query),
|
||||
spec: {
|
||||
[LEGACY_STRING_VALUE_KEY]: query,
|
||||
},
|
||||
@@ -332,6 +315,7 @@ export function sceneVariablesSetToSchemaV2Variables(
|
||||
options,
|
||||
query: dataQuery,
|
||||
definition: variable.state.definition,
|
||||
datasource: getElementDatasource(set, variable, 'variable', undefined, dsReferencesMapping),
|
||||
sort: transformSortVariableToEnum(variable.state.sort),
|
||||
refresh: transformVariableRefreshToEnum(variable.state.refresh),
|
||||
regex: variable.state.regex,
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
CustomVariableKind,
|
||||
Spec as DashboardV2Spec,
|
||||
DatasourceVariableKind,
|
||||
defaultDataQueryKind,
|
||||
GridLayoutItemSpec,
|
||||
GridLayoutSpec,
|
||||
GroupByVariableKind,
|
||||
@@ -139,7 +138,6 @@ describe('transformSaveModelSchemaV2ToScene', () => {
|
||||
// Variables
|
||||
const variables = scene.state?.$variables;
|
||||
expect(variables?.state.variables).toHaveLength(dash.variables.length);
|
||||
|
||||
validateVariable({
|
||||
sceneVariable: variables?.state.variables[0],
|
||||
variableKind: dash.variables[0] as QueryVariableKind,
|
||||
@@ -208,26 +206,36 @@ describe('transformSaveModelSchemaV2ToScene', () => {
|
||||
// Annotations
|
||||
expect(scene.state.$data).toBeInstanceOf(DashboardDataLayerSet);
|
||||
const dataLayers = scene.state.$data as DashboardDataLayerSet;
|
||||
// we should get two annotations, Grafana built-in and the custom ones
|
||||
expect(dataLayers.state.annotationLayers).toHaveLength(dash.annotations.length);
|
||||
expect(dataLayers.state.annotationLayers[0].state.name).toBe(dash.annotations[0].spec.name);
|
||||
expect(dataLayers.state.annotationLayers[0].state.isEnabled).toBe(dash.annotations[0].spec.enable);
|
||||
expect(dataLayers.state.annotationLayers[0].state.isHidden).toBe(dash.annotations[0].spec.hide);
|
||||
expect(dataLayers.state.annotationLayers).toHaveLength(5);
|
||||
|
||||
// Built-in
|
||||
const builtInAnnotation = dataLayers.state.annotationLayers[0] as unknown as DashboardAnnotationsDataLayer;
|
||||
expect(builtInAnnotation.state.name).toBe('Annotations & Alerts');
|
||||
expect(builtInAnnotation.state.isEnabled).toBe(true);
|
||||
expect(builtInAnnotation.state.isHidden).toBe(true);
|
||||
expect(builtInAnnotation.state?.query.builtIn).toBe(1);
|
||||
|
||||
// Enabled
|
||||
expect(dataLayers.state.annotationLayers[1].state.name).toBe(dash.annotations[1].spec.name);
|
||||
expect(dataLayers.state.annotationLayers[1].state.isEnabled).toBe(dash.annotations[1].spec.enable);
|
||||
expect(dataLayers.state.annotationLayers[1].state.isHidden).toBe(dash.annotations[1].spec.hide);
|
||||
|
||||
// Disabled
|
||||
expect(dataLayers.state.annotationLayers[2].state.name).toBe(dash.annotations[2].spec.name);
|
||||
expect(dataLayers.state.annotationLayers[2].state.isEnabled).toBe(dash.annotations[2].spec.enable);
|
||||
expect(dataLayers.state.annotationLayers[2].state.isHidden).toBe(dash.annotations[2].spec.hide);
|
||||
|
||||
// Hidden
|
||||
// Disabled
|
||||
expect(dataLayers.state.annotationLayers[3].state.name).toBe(dash.annotations[3].spec.name);
|
||||
expect(dataLayers.state.annotationLayers[3].state.isEnabled).toBe(dash.annotations[3].spec.enable);
|
||||
expect(dataLayers.state.annotationLayers[3].state.isHidden).toBe(dash.annotations[3].spec.hide);
|
||||
|
||||
// Hidden
|
||||
expect(dataLayers.state.annotationLayers[4].state.name).toBe(dash.annotations[4].spec.name);
|
||||
expect(dataLayers.state.annotationLayers[4].state.isEnabled).toBe(dash.annotations[4].spec.enable);
|
||||
expect(dataLayers.state.annotationLayers[4].state.isHidden).toBe(dash.annotations[4].spec.hide);
|
||||
|
||||
// VizPanel
|
||||
const vizPanels = (scene.state.body as DashboardLayoutManager).getVizPanels();
|
||||
expect(vizPanels).toHaveLength(2);
|
||||
@@ -271,14 +279,13 @@ describe('transformSaveModelSchemaV2ToScene', () => {
|
||||
kind: 'PanelQuery',
|
||||
spec: {
|
||||
refId: 'A',
|
||||
datasource: {
|
||||
type: 'graphite',
|
||||
uid: 'datasource1',
|
||||
},
|
||||
hidden: false,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
version: defaultDataQueryKind().version,
|
||||
group: 'graphite',
|
||||
datasource: {
|
||||
name: 'datasource1',
|
||||
},
|
||||
kind: 'prometheus',
|
||||
spec: {
|
||||
expr: 'test-query',
|
||||
},
|
||||
@@ -300,14 +307,13 @@ describe('transformSaveModelSchemaV2ToScene', () => {
|
||||
kind: 'PanelQuery',
|
||||
spec: {
|
||||
refId: 'A',
|
||||
datasource: {
|
||||
type: 'prometheus',
|
||||
uid: 'datasource1',
|
||||
},
|
||||
hidden: false,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
version: defaultDataQueryKind().version,
|
||||
group: 'prometheus',
|
||||
datasource: {
|
||||
name: 'datasource1',
|
||||
},
|
||||
kind: 'prometheus',
|
||||
spec: {
|
||||
expr: 'test-query',
|
||||
},
|
||||
@@ -334,12 +340,7 @@ describe('transformSaveModelSchemaV2ToScene', () => {
|
||||
refId: 'A',
|
||||
hidden: false,
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
version: defaultDataQueryKind().version,
|
||||
group: 'prometheus',
|
||||
datasource: {
|
||||
name: 'abc123',
|
||||
},
|
||||
kind: 'prometheus',
|
||||
spec: {
|
||||
expr: 'test-query',
|
||||
},
|
||||
@@ -721,6 +722,10 @@ describe('transformSaveModelSchemaV2ToScene', () => {
|
||||
enable: true,
|
||||
hide: false,
|
||||
iconColor: 'purple',
|
||||
datasource: {
|
||||
type: 'prometheus',
|
||||
uid: 'abc123',
|
||||
},
|
||||
legacyOptions: {
|
||||
expr: 'rate(http_requests_total[5m])',
|
||||
queryType: 'range',
|
||||
@@ -728,15 +733,6 @@ describe('transformSaveModelSchemaV2ToScene', () => {
|
||||
useValueAsTime: true,
|
||||
step: '1m',
|
||||
},
|
||||
query: {
|
||||
kind: 'DataQuery',
|
||||
version: defaultDataQueryKind().version,
|
||||
group: 'prometheus',
|
||||
datasource: {
|
||||
name: 'abc123',
|
||||
},
|
||||
spec: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -768,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}}',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ Utilities / functions for working with dagger pipelines
|
||||
def with_dagger_install(commands = [], dagger_version = ""):
|
||||
return [
|
||||
"wget -qO- https://github.com/dagger/dagger/releases/download/{}/dagger_{}_linux_amd64.tar.gz | tar zx -C /bin".format(dagger_version, dagger_version),
|
||||
"apk add docker",
|
||||
"apk add docker bash",
|
||||
] + commands
|
||||
|
||||
@@ -210,11 +210,25 @@ def rgm_main():
|
||||
)
|
||||
|
||||
def rgm_tag():
|
||||
# Runs a package / build process (with all distros) when a tag is made
|
||||
"""Tag release pipeline that builds and packages all distributions.
|
||||
|
||||
Returns:
|
||||
Drone pipeline.
|
||||
"""
|
||||
generate_token_step = github_app_generate_token_step()
|
||||
build_steps = rgm_run("rgm-build", "drone_build_tag_grafana.sh")
|
||||
|
||||
# Add dependency on token generation step
|
||||
for step in build_steps:
|
||||
step["depends_on"] = [generate_token_step["name"]]
|
||||
|
||||
steps = [generate_token_step] + build_steps
|
||||
|
||||
return pipeline(
|
||||
name = "rgm-tag-prerelease",
|
||||
trigger = tag_trigger,
|
||||
steps = rgm_run("rgm-build", "drone_build_tag_grafana.sh"),
|
||||
steps = steps,
|
||||
volumes = github_app_step_volumes() + github_app_pipeline_volumes(),
|
||||
)
|
||||
|
||||
def rgm_version_branch():
|
||||
|
||||
Reference in New Issue
Block a user