diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index ec656436ee7..6b40b814064 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -440,6 +440,7 @@ i18next.config.ts @grafana/grafana-frontend-platform
/e2e-playwright/dashboards/TestDashboard.json @grafana/dashboards-squad @grafana/grafana-search-navigate-organise
/e2e-playwright/dashboards/TestV2Dashboard.json @grafana/dashboards-squad
/e2e-playwright/dashboards/V2DashWithRepeats.json @grafana/dashboards-squad
+/e2e-playwright/dashboards/V2DashWithRowRepeats.json @grafana/dashboards-squad
/e2e-playwright/dashboards/V2DashWithTabRepeats.json @grafana/dashboards-squad
/e2e-playwright/dashboards-suite/adhoc-filter-from-panel.spec.ts @grafana/datapro
/e2e-playwright/dashboards-suite/dashboard-browse-nested.spec.ts @grafana/grafana-search-navigate-organise
@@ -542,6 +543,7 @@ i18next.config.ts @grafana/grafana-frontend-platform
/packages/grafana-data/tsconfig.json @grafana/grafana-frontend-platform
/packages/grafana-data/test/ @grafana/grafana-frontend-platform
/packages/grafana-data/typings/ @grafana/grafana-frontend-platform
+/packages/grafana-data/scripts/ @grafana/grafana-frontend-platform
/packages/grafana-data/src/**/*logs* @grafana/observability-logs
/packages/grafana-data/src/context/plugins/ @grafana/plugins-platform-frontend
@@ -657,6 +659,7 @@ i18next.config.ts @grafana/grafana-frontend-platform
/packages/grafana-runtime/src/services/LocationService.tsx @grafana/grafana-search-navigate-organise
/packages/grafana-runtime/src/services/LocationSrv.ts @grafana/grafana-search-navigate-organise
/packages/grafana-runtime/src/services/live.ts @grafana/dashboards-squad
+/packages/grafana-runtime/src/services/pluginMeta @grafana/plugins-platform-frontend
/packages/grafana-runtime/src/utils/chromeHeaderHeight.ts @grafana/grafana-search-navigate-organise
/packages/grafana-runtime/src/utils/DataSourceWithBackend* @grafana/grafana-datasources-core-services
/packages/grafana-runtime/src/utils/licensing.ts @grafana/grafana-operator-experience-squad
@@ -1275,6 +1278,7 @@ embed.go @grafana/grafana-as-code
/.github/workflows/i18n-crowdin-download.yml @grafana/grafana-frontend-platform
/.github/workflows/i18n-crowdin-create-tasks.yml @grafana/grafana-frontend-platform
/.github/workflows/i18n-verify.yml @grafana/grafana-frontend-platform
+/.github/workflows/deploy-storybook.yml @grafana/grafana-frontend-platform
/.github/workflows/deploy-storybook-preview.yml @grafana/grafana-frontend-platform
/.github/workflows/scripts/crowdin/create-tasks.ts @grafana/grafana-frontend-platform
/.github/workflows/scripts/publish-frontend-metrics.mts @grafana/grafana-frontend-platform
diff --git a/.github/workflows/deploy-storybook.yml b/.github/workflows/deploy-storybook.yml
new file mode 100644
index 00000000000..08bffaeb891
--- /dev/null
+++ b/.github/workflows/deploy-storybook.yml
@@ -0,0 +1,79 @@
+name: Deploy Storybook
+
+on:
+ workflow_dispatch:
+ # push:
+ # branches:
+ # - main
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions: {}
+
+jobs:
+ detect-changes:
+ # Only run in grafana/grafana
+ if: github.repository == 'grafana/grafana'
+ name: Detect whether code changed
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ outputs:
+ changed-frontend-packages: ${{ steps.detect-changes.outputs.frontend-packages }}
+ steps:
+ - uses: actions/checkout@v5
+ with:
+ persist-credentials: true # required to get more history in the changed-files action
+ fetch-depth: 2
+ - name: Detect changes
+ id: detect-changes
+ uses: ./.github/actions/change-detection
+ with:
+ self: .github/workflows/deploy-storybook.yml
+ deploy-storybook:
+ name: Deploy Storybook
+ runs-on: ubuntu-latest
+ needs: detect-changes
+ # Only run in grafana/grafana
+ if: github.repository == 'grafana/grafana' && needs.detect-changes.outputs.changed-frontend-packages == 'true'
+ permissions:
+ contents: read
+ id-token: write
+
+ env:
+ BUCKET_NAME: grafana-storybook
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v5
+ with:
+ persist-credentials: false
+
+ - name: Setup Node.js
+ uses: ./.github/actions/setup-node
+
+ - name: Install dependencies
+ run: yarn install --immutable
+
+ - name: Build storybook
+ run: yarn storybook:build
+
+ # Create the GCS folder name
+ # Right now, this just returns "canary"
+ # But we'll expand this to work for "latest" as well in the future
+ - name: Create deploy name
+ id: create-deploy-name
+ run: |
+ echo "deploy-name=canary" >> "$GITHUB_OUTPUT"
+
+ - name: Upload Storybook
+ uses: grafana/shared-workflows/actions/push-to-gcs@main
+ with:
+ environment: prod
+ bucket: ${{ env.BUCKET_NAME }}
+ bucket_path: ${{ steps.create-deploy-name.outputs.deploy-name }}
+ path: packages/grafana-ui/dist/storybook
+ service_account: github-gf-storybook-deploy@grafanalabs-workload-identity.iam.gserviceaccount.com
+ parent: false
diff --git a/.golangci.yml b/.golangci.yml
index 069e88632ff..d7037bf6fac 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -121,6 +121,8 @@ linters:
- '**/pkg/tsdb/zipkin/**/*'
- '**/pkg/tsdb/jaeger/*'
- '**/pkg/tsdb/jaeger/**/*'
+ - '**/pkg/tsdb/elasticsearch/*'
+ - '**/pkg/tsdb/elasticsearch/**/*'
deny:
- pkg: github.com/grafana/grafana/pkg/api
desc: Core plugins are not allowed to depend on Grafana core packages
diff --git a/Makefile b/Makefile
index a5353e95567..e4a261acb4b 100644
--- a/Makefile
+++ b/Makefile
@@ -135,7 +135,7 @@ i18n-extract-enterprise:
@echo "Skipping i18n extract for Enterprise: not enabled"
else
i18n-extract-enterprise:
- @echo "Extracting i18n strings for Enterprise"
+ @echo "Extracting i18n strings for Enterprise"
cd public/locales/enterprise && yarn run i18next-cli extract --sync-primary
endif
@@ -227,6 +227,10 @@ fix-cue:
gen-jsonnet:
go generate ./devenv/jsonnet
+.PHONY: gen-themes
+gen-themes:
+ go generate ./pkg/services/preference
+
.PHONY: update-workspace
update-workspace: gen-go
@echo "updating workspace"
@@ -244,6 +248,7 @@ build-go-fast: ## Build all Go binaries without updating workspace.
.PHONY: build-backend
build-backend: ## Build Grafana backend.
@echo "build backend"
+ $(MAKE) gen-themes
$(GO) run build.go $(GO_BUILD_FLAGS) build-backend
.PHONY: build-air
diff --git a/apps/advisor/pkg/app/checks/datasourcecheck/check.go b/apps/advisor/pkg/app/checks/datasourcecheck/check.go
index cae29e181fd..c35c47e45f6 100644
--- a/apps/advisor/pkg/app/checks/datasourcecheck/check.go
+++ b/apps/advisor/pkg/app/checks/datasourcecheck/check.go
@@ -28,7 +28,7 @@ type check struct {
PluginStore pluginstore.Store
PluginContextProvider PluginContextProvider
PluginClient plugins.Client
- PluginRepo repo.Service
+ PluginRepo checks.PluginInfoGetter
GrafanaVersion string
pluginCanBeInstalledCache map[string]bool
pluginExistsCacheMu sync.RWMutex
@@ -39,7 +39,7 @@ func New(
pluginStore pluginstore.Store,
pluginContextProvider PluginContextProvider,
pluginClient plugins.Client,
- pluginRepo repo.Service,
+ pluginRepo checks.PluginInfoGetter,
grafanaVersion string,
) checks.Check {
return &check{
diff --git a/apps/advisor/pkg/app/checks/datasourcecheck/missing_plugin_step.go b/apps/advisor/pkg/app/checks/datasourcecheck/missing_plugin_step.go
index 1d784f0a544..9b70f5d0896 100644
--- a/apps/advisor/pkg/app/checks/datasourcecheck/missing_plugin_step.go
+++ b/apps/advisor/pkg/app/checks/datasourcecheck/missing_plugin_step.go
@@ -15,7 +15,7 @@ import (
type missingPluginStep struct {
PluginStore pluginstore.Store
- PluginRepo repo.Service
+ PluginRepo checks.PluginInfoGetter
GrafanaVersion string
}
diff --git a/apps/advisor/pkg/app/checks/ifaces.go b/apps/advisor/pkg/app/checks/ifaces.go
index 6573253b557..2b205933151 100644
--- a/apps/advisor/pkg/app/checks/ifaces.go
+++ b/apps/advisor/pkg/app/checks/ifaces.go
@@ -5,6 +5,7 @@ import (
"github.com/grafana/grafana-app-sdk/logging"
advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
+ "github.com/grafana/grafana/pkg/plugins/repo"
)
// Check returns metadata about the check being executed and the list of Steps
@@ -37,3 +38,10 @@ type Step interface {
// Run executes the step for an item and returns a report
Run(ctx context.Context, log logging.Logger, obj *advisorv0alpha1.CheckSpec, item any) ([]advisorv0alpha1.CheckReportFailure, error)
}
+
+// PluginInfoGetter is a minimal interface for retrieving plugin information from a repository.
+// It contains only the GetPluginsInfo method used by plugincheck and datasourcecheck.
+type PluginInfoGetter interface {
+ // GetPluginsInfo will return a list of plugins from grafana.com/api/plugins.
+ GetPluginsInfo(ctx context.Context, options repo.GetPluginsInfoOptions, compatOpts repo.CompatOpts) ([]repo.PluginInfo, error)
+}
diff --git a/apps/advisor/pkg/app/checks/plugincheck/check.go b/apps/advisor/pkg/app/checks/plugincheck/check.go
index 3d261f81b67..00bc293e86c 100644
--- a/apps/advisor/pkg/app/checks/plugincheck/check.go
+++ b/apps/advisor/pkg/app/checks/plugincheck/check.go
@@ -17,7 +17,7 @@ const (
func New(
pluginStore pluginstore.Store,
- pluginRepo repo.Service,
+ pluginRepo checks.PluginInfoGetter,
updateChecker pluginchecker.PluginUpdateChecker,
pluginErrorResolver plugins.ErrorResolver,
grafanaVersion string,
@@ -33,7 +33,7 @@ func New(
type check struct {
PluginStore pluginstore.Store
- PluginRepo repo.Service
+ PluginRepo checks.PluginInfoGetter
updateChecker pluginchecker.PluginUpdateChecker
pluginErrorResolver plugins.ErrorResolver
GrafanaVersion string
diff --git a/apps/alerting/historian/go.mod b/apps/alerting/historian/go.mod
index a79829d45c2..9bce171ed16 100644
--- a/apps/alerting/historian/go.mod
+++ b/apps/alerting/historian/go.mod
@@ -4,7 +4,7 @@ go 1.25.5
require (
github.com/go-kit/log v0.2.1
- github.com/grafana/alerting v0.0.0-20251231150637-b7821017d69f
+ github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4
github.com/grafana/grafana-app-sdk v0.48.7
github.com/grafana/grafana-app-sdk/logging v0.48.7
diff --git a/apps/alerting/historian/go.sum b/apps/alerting/historian/go.sum
index d45d418dfb8..73b2a5c991b 100644
--- a/apps/alerting/historian/go.sum
+++ b/apps/alerting/historian/go.sum
@@ -243,8 +243,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/grafana/alerting v0.0.0-20251231150637-b7821017d69f h1:Br4SaUL3dnVopKKNhDavCLgehw60jdtl/sIxdfzmVts=
-github.com/grafana/alerting v0.0.0-20251231150637-b7821017d69f/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU=
+github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f h1:3bXOyht68qkfvD6Y8z8XoenFbytSSOIkr/s+AqRzj0o=
+github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f/go.mod h1:Ji0SfJChcwjgq8ljy6Y5CcYfHfAYKXjKYeysOoDS/6s=
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 h1:jSojuc7njleS3UOz223WDlXOinmuLAIPI0z2vtq8EgI=
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4/go.mod h1:VahT+GtfQIM+o8ht2StR6J9g+Ef+C2Vokh5uuSmOD/4=
github.com/grafana/grafana-app-sdk v0.48.7 h1:9mF7nqkqP0QUYYDlznoOt+GIyjzj45wGfUHB32u2ZMo=
diff --git a/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue b/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue
index 6488de41c96..55094fe3447 100644
--- a/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue
+++ b/apps/dashboard/kinds/v2alpha1/dashboard_spec.cue
@@ -254,8 +254,18 @@ FieldConfig: {
// custom is specified by the FieldConfig field
// in panel plugin schemas.
custom?: {...}
+
+ // Calculate min max per field
+ fieldMinMax?: bool
+
+ // How null values should be handled when calculating field stats
+ // "null" - Include null values, "connected" - Ignore nulls, "null as zero" - Treat nulls as zero
+ nullValueMode?: NullValueMode
}
+// How null values should be handled
+NullValueMode: "null" | "connected" | "null as zero"
+
DynamicConfigValue: {
id: string | *""
value?: _
diff --git a/apps/dashboard/kinds/v2beta1/dashboard_spec.cue b/apps/dashboard/kinds/v2beta1/dashboard_spec.cue
index a8e1f121213..0802430907e 100644
--- a/apps/dashboard/kinds/v2beta1/dashboard_spec.cue
+++ b/apps/dashboard/kinds/v2beta1/dashboard_spec.cue
@@ -250,8 +250,18 @@ FieldConfig: {
// custom is specified by the FieldConfig field
// in panel plugin schemas.
custom?: {...}
+
+ // Calculate min max per field
+ fieldMinMax?: bool
+
+ // How null values should be handled when calculating field stats
+ // "null" - Include null values, "connected" - Ignore nulls, "null as zero" - Treat nulls as zero
+ nullValueMode?: NullValueMode
}
+// How null values should be handled
+NullValueMode: "null" | "connected" | "null as zero"
+
DynamicConfigValue: {
id: string | *""
value?: _
diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue
index 2b027ff98e1..293082ab82f 100644
--- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue
+++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec.cue
@@ -258,8 +258,18 @@ FieldConfig: {
// custom is specified by the FieldConfig field
// in panel plugin schemas.
custom?: {...}
+
+ // Calculate min max per field
+ fieldMinMax?: bool
+
+ // How null values should be handled when calculating field stats
+ // "null" - Include null values, "connected" - Ignore nulls, "null as zero" - Treat nulls as zero
+ nullValueMode?: NullValueMode
}
+// How null values should be handled
+NullValueMode: "null" | "connected" | "null as zero"
+
DynamicConfigValue: {
id: string | *""
value?: _
diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go
index 3f594306ef5..f7ccfdd4925 100644
--- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go
+++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_spec_gen.go
@@ -419,6 +419,11 @@ type DashboardFieldConfig struct {
// custom is specified by the FieldConfig field
// in panel plugin schemas.
Custom map[string]interface{} `json:"custom,omitempty"`
+ // Calculate min max per field
+ FieldMinMax *bool `json:"fieldMinMax,omitempty"`
+ // How null values should be handled when calculating field stats
+ // "null" - Include null values, "connected" - Ignore nulls, "null as zero" - Treat nulls as zero
+ NullValueMode *DashboardNullValueMode `json:"nullValueMode,omitempty"`
}
// NewDashboardFieldConfig creates a new DashboardFieldConfig object.
@@ -745,6 +750,16 @@ func NewDashboardActionVariable() *DashboardActionVariable {
// +k8s:openapi-gen=true
const DashboardActionVariableType = "string"
+// How null values should be handled
+// +k8s:openapi-gen=true
+type DashboardNullValueMode string
+
+const (
+ DashboardNullValueModeNull DashboardNullValueMode = "null"
+ DashboardNullValueModeConnected DashboardNullValueMode = "connected"
+ DashboardNullValueModeNullAsZero DashboardNullValueMode = "null as zero"
+)
+
// +k8s:openapi-gen=true
type DashboardDynamicConfigValue struct {
Id string `json:"id"`
diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go
index 4c6f3f5ed20..926d50cb49d 100644
--- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go
+++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go
@@ -2277,6 +2277,20 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardFieldConfig(ref common.Referenc
},
},
},
+ "fieldMinMax": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Calculate min max per field",
+ Type: []string{"boolean"},
+ Format: "",
+ },
+ },
+ "nullValueMode": {
+ SchemaProps: spec.SchemaProps{
+ Description: "How null values should be handled when calculating field stats \"null\" - Include null values, \"connected\" - Ignore nulls, \"null as zero\" - Treat nulls as zero",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
},
},
},
diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue
index 375ba67f003..41ab7bc3fa7 100644
--- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue
+++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue
@@ -254,8 +254,18 @@ FieldConfig: {
// custom is specified by the FieldConfig field
// in panel plugin schemas.
custom?: {...}
+
+ // Calculate min max per field
+ fieldMinMax?: bool
+
+ // How null values should be handled when calculating field stats
+ // "null" - Include null values, "connected" - Ignore nulls, "null as zero" - Treat nulls as zero
+ nullValueMode?: NullValueMode
}
+// How null values should be handled
+NullValueMode: "null" | "connected" | "null as zero"
+
DynamicConfigValue: {
id: string | *""
value?: _
diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go
index 96054cb2fc4..06f1e1df599 100644
--- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go
+++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go
@@ -423,6 +423,11 @@ type DashboardFieldConfig struct {
// custom is specified by the FieldConfig field
// in panel plugin schemas.
Custom map[string]interface{} `json:"custom,omitempty"`
+ // Calculate min max per field
+ FieldMinMax *bool `json:"fieldMinMax,omitempty"`
+ // How null values should be handled when calculating field stats
+ // "null" - Include null values, "connected" - Ignore nulls, "null as zero" - Treat nulls as zero
+ NullValueMode *DashboardNullValueMode `json:"nullValueMode,omitempty"`
}
// NewDashboardFieldConfig creates a new DashboardFieldConfig object.
@@ -749,6 +754,16 @@ func NewDashboardActionVariable() *DashboardActionVariable {
// +k8s:openapi-gen=true
const DashboardActionVariableType = "string"
+// How null values should be handled
+// +k8s:openapi-gen=true
+type DashboardNullValueMode string
+
+const (
+ DashboardNullValueModeNull DashboardNullValueMode = "null"
+ DashboardNullValueModeConnected DashboardNullValueMode = "connected"
+ DashboardNullValueModeNullAsZero DashboardNullValueMode = "null as zero"
+)
+
// +k8s:openapi-gen=true
type DashboardDynamicConfigValue struct {
Id string `json:"id"`
diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go
index 402810f6e53..73c4d1f7349 100644
--- a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go
+++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go
@@ -2284,6 +2284,20 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfig(ref common.Reference
},
},
},
+ "fieldMinMax": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Calculate min max per field",
+ Type: []string{"boolean"},
+ Format: "",
+ },
+ },
+ "nullValueMode": {
+ SchemaProps: spec.SchemaProps{
+ Description: "How null values should be handled when calculating field stats \"null\" - Include null values, \"connected\" - Ignore nulls, \"null as zero\" - Treat nulls as zero",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
},
},
},
diff --git a/apps/dashboard/pkg/apis/dashboard_manifest.go b/apps/dashboard/pkg/apis/dashboard_manifest.go
index e94d66fec82..aee89730dc1 100644
--- a/apps/dashboard/pkg/apis/dashboard_manifest.go
+++ b/apps/dashboard/pkg/apis/dashboard_manifest.go
@@ -32,10 +32,10 @@ var (
rawSchemaDashboardv1beta1 = []byte(`{"ConversionStatus":{"additionalProperties":false,"description":"ConversionStatus is the status of the conversion of the dashboard.","properties":{"error":{"description":"The error message from the conversion.\nEmpty if the conversion has not failed.","type":"string"},"failed":{"description":"Whether from another version has failed.\nIf true, means that the dashboard is not valid,\nand the caller should instead fetch the stored version.","type":"boolean"},"source":{"additionalProperties":{},"description":"The original value map[string]any","type":"object"},"storedVersion":{"description":"The version which was stored when the dashboard was created / updated.\nFetching this version should always succeed.","type":"string"}},"required":["failed"],"type":"object"},"Dashboard":{"properties":{"spec":{"$ref":"#/components/schemas/spec"},"status":{"$ref":"#/components/schemas/status"}},"required":["spec"]},"spec":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"},"status":{"additionalProperties":false,"properties":{"conversion":{"$ref":"#/components/schemas/ConversionStatus","description":"Optional conversion status."}},"type":"object"}}`)
versionSchemaDashboardv1beta1 app.VersionSchema
_ = json.Unmarshal(rawSchemaDashboardv1beta1, &versionSchemaDashboardv1beta1)
- rawSchemaDashboardv2alpha1 = []byte(`{"Action":{"additionalProperties":false,"properties":{"confirmation":{"type":"string"},"fetch":{"$ref":"#/components/schemas/FetchOptions"},"infinity":{"$ref":"#/components/schemas/InfinityOptions"},"oneClick":{"type":"boolean"},"style":{"additionalProperties":false,"properties":{"backgroundColor":{"type":"string"}},"type":"object"},"title":{"type":"string"},"type":{"$ref":"#/components/schemas/ActionType"},"variables":{"items":{"$ref":"#/components/schemas/ActionVariable"},"type":"array"}},"required":["type","title"],"type":"object"},"ActionType":{"enum":["fetch","infinity"],"type":"string"},"ActionVariable":{"additionalProperties":false,"properties":{"key":{"type":"string"},"name":{"type":"string"},"type":{"$ref":"#/components/schemas/ActionVariableType"}},"required":["key","name","type"],"type":"object"},"ActionVariableType":{"const":"string","description":"Action variable type","type":"string"},"AdHocFilterWithLabels":{"additionalProperties":false,"description":"Define the AdHocFilterWithLabels type","properties":{"condition":{"description":"@deprecated","type":"string"},"forceEdit":{"type":"boolean"},"key":{"type":"string"},"keyLabel":{"type":"string"},"operator":{"type":"string"},"origin":{"$ref":"#/components/schemas/FilterOrigin"},"value":{"type":"string"},"valueLabels":{"items":{"type":"string"},"type":"array"},"values":{"items":{"type":"string"},"type":"array"}},"required":["key","operator","value"],"type":"object"},"AdhocVariableKind":{"additionalProperties":false,"description":"Adhoc variable kind","properties":{"kind":{"const":"AdhocVariable","type":"string"},"spec":{"$ref":"#/components/schemas/AdhocVariableSpec"}},"required":["kind","spec"],"type":"object"},"AdhocVariableSpec":{"additionalProperties":false,"description":"Adhoc variable specification","properties":{"allowCustomValue":{"default":true,"type":"boolean"},"baseFilters":{"items":{"$ref":"#/components/schemas/AdHocFilterWithLabels"},"type":"array"},"datasource":{"$ref":"#/components/schemas/DataSourceRef"},"defaultKeys":{"items":{"$ref":"#/components/schemas/MetricFindValue"},"type":"array"},"description":{"type":"string"},"filters":{"items":{"$ref":"#/components/schemas/AdHocFilterWithLabels"},"type":"array"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","baseFilters","filters","defaultKeys","hide","skipUrlSync","allowCustomValue"],"type":"object"},"AnnotationEventFieldMapping":{"additionalProperties":false,"description":"Annotation event field mapping. Defines how to map a data frame field to an annotation event field.","properties":{"regex":{"description":"Regular expression to apply to the field value","type":"string"},"source":{"default":"field","description":"Source type for the field value","type":"string"},"value":{"description":"Constant value to use when source is \"text\"","type":"string"}},"type":"object"},"AnnotationPanelFilter":{"additionalProperties":false,"properties":{"exclude":{"default":false,"description":"Should the specified panels be included or excluded","type":"boolean"},"ids":{"description":"Panel IDs that should be included or excluded","items":{"type":"integer"},"type":"array"}},"required":["ids"],"type":"object"},"AnnotationQueryKind":{"additionalProperties":false,"properties":{"kind":{"const":"AnnotationQuery","type":"string"},"spec":{"$ref":"#/components/schemas/AnnotationQuerySpec"}},"required":["kind","spec"],"type":"object"},"AnnotationQuerySpec":{"additionalProperties":false,"properties":{"builtIn":{"default":false,"type":"boolean"},"datasource":{"$ref":"#/components/schemas/DataSourceRef"},"enable":{"type":"boolean"},"filter":{"$ref":"#/components/schemas/AnnotationPanelFilter"},"hide":{"type":"boolean"},"iconColor":{"type":"string"},"legacyOptions":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"Catch-all field for datasource-specific properties","type":"object"},"mappings":{"additionalProperties":{"$ref":"#/components/schemas/AnnotationEventFieldMapping"},"description":"Mappings define how to convert data frame fields to annotation event fields.","type":"object"},"name":{"type":"string"},"query":{"$ref":"#/components/schemas/DataQueryKind"}},"required":["enable","hide","iconColor","name"],"type":"object"},"AutoGridLayoutItemKind":{"additionalProperties":false,"properties":{"kind":{"const":"AutoGridLayoutItem","type":"string"},"spec":{"$ref":"#/components/schemas/AutoGridLayoutItemSpec"}},"required":["kind","spec"],"type":"object"},"AutoGridLayoutItemSpec":{"additionalProperties":false,"properties":{"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"element":{"$ref":"#/components/schemas/ElementReference"},"repeat":{"$ref":"#/components/schemas/AutoGridRepeatOptions"}},"required":["element"],"type":"object"},"AutoGridLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"AutoGridLayout","type":"string"},"spec":{"$ref":"#/components/schemas/AutoGridLayoutSpec"}},"required":["kind","spec"],"type":"object"},"AutoGridLayoutSpec":{"additionalProperties":false,"properties":{"columnWidth":{"type":"number"},"columnWidthMode":{"default":"standard","enum":["narrow","standard","wide","custom"],"type":"string"},"fillScreen":{"default":false,"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/AutoGridLayoutItemKind"},"type":"array"},"maxColumnCount":{"default":3,"type":"number"},"rowHeight":{"type":"number"},"rowHeightMode":{"default":"standard","enum":["short","standard","tall","custom"],"type":"string"}},"required":["columnWidthMode","rowHeightMode","items"],"type":"object"},"AutoGridRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"ConditionalRenderingDataKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingData","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingDataSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingDataSpec":{"additionalProperties":false,"properties":{"value":{"type":"boolean"}},"required":["value"],"type":"object"},"ConditionalRenderingGroupKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingGroup","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingGroupSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingGroupSpec":{"additionalProperties":false,"properties":{"condition":{"enum":["and","or"],"type":"string"},"items":{"items":{"oneOf":[{"$ref":"#/components/schemas/ConditionalRenderingVariableKind"},{"$ref":"#/components/schemas/ConditionalRenderingDataKind"},{"$ref":"#/components/schemas/ConditionalRenderingTimeRangeSizeKind"}]},"type":"array"},"visibility":{"enum":["show","hide"],"type":"string"}},"required":["visibility","condition","items"],"type":"object"},"ConditionalRenderingTimeRangeSizeKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingTimeRangeSize","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingTimeRangeSizeSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingTimeRangeSizeSpec":{"additionalProperties":false,"properties":{"value":{"type":"string"}},"required":["value"],"type":"object"},"ConditionalRenderingVariableKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingVariable","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingVariableSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingVariableSpec":{"additionalProperties":false,"properties":{"operator":{"enum":["equals","notEquals","matches","notMatches"],"type":"string"},"value":{"type":"string"},"variable":{"type":"string"}},"required":["variable","operator","value"],"type":"object"},"ConstantVariableKind":{"additionalProperties":false,"description":"Constant variable kind","properties":{"kind":{"const":"ConstantVariable","type":"string"},"spec":{"$ref":"#/components/schemas/ConstantVariableSpec"}},"required":["kind","spec"],"type":"object"},"ConstantVariableSpec":{"additionalProperties":false,"description":"Constant variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","query","current","hide","skipUrlSync"],"type":"object"},"ConversionStatus":{"additionalProperties":false,"description":"ConversionStatus is the status of the conversion of the dashboard.","properties":{"error":{"description":"The error message from the conversion.\nEmpty if the conversion has not failed.","type":"string"},"failed":{"description":"Whether from another version has failed.\nIf true, means that the dashboard is not valid,\nand the caller should instead fetch the stored version.","type":"boolean"},"source":{"additionalProperties":{},"description":"The original value map[string]any","type":"object"},"storedVersion":{"description":"The version which was stored when the dashboard was created / updated.\nFetching this version should always succeed.","type":"string"}},"required":["failed"],"type":"object"},"CustomVariableKind":{"additionalProperties":false,"description":"Custom variable kind","properties":{"kind":{"const":"CustomVariable","type":"string"},"spec":{"$ref":"#/components/schemas/CustomVariableSpec"}},"required":["kind","spec"],"type":"object"},"CustomVariableSpec":{"additionalProperties":false,"description":"Custom variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"},"valuesFormat":{"enum":["csv","json"],"type":"string"}},"required":["name","query","current","options","multi","includeAll","hide","skipUrlSync","allowCustomValue"],"type":"object"},"Dashboard":{"properties":{"spec":{"$ref":"#/components/schemas/spec"},"status":{"$ref":"#/components/schemas/status"}},"required":["spec"]},"DashboardCursorSync":{"description":"\"Off\" for no shared crosshair or tooltip (default).\n\"Crosshair\" for shared crosshair.\n\"Tooltip\" for shared crosshair AND shared tooltip.","enum":["Crosshair","Tooltip","Off"],"type":"string"},"DashboardLink":{"additionalProperties":false,"description":"Links with references to other dashboards or external resources","properties":{"asDropdown":{"default":false,"description":"If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards","type":"boolean"},"icon":{"description":"Icon name to be displayed with the link","type":"string"},"includeVars":{"default":false,"description":"If true, includes current template variables values in the link as query params","type":"boolean"},"keepTime":{"default":false,"description":"If true, includes current time range in the link as query params","type":"boolean"},"placement":{"$ref":"#/components/schemas/DashboardLinkPlacement","description":"Placement can be used to display the link somewhere else on the dashboard other than above the visualisations."},"tags":{"description":"List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards","items":{"type":"string"},"type":"array"},"targetBlank":{"default":false,"description":"If true, the link will be opened in a new tab","type":"boolean"},"title":{"description":"Title to display with the link","type":"string"},"tooltip":{"description":"Tooltip to display when the user hovers their mouse over it","type":"string"},"type":{"$ref":"#/components/schemas/DashboardLinkType","description":"Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)\nFIXME: The type is generated as ` + "`" + `type: DashboardLinkType | dashboardLinkType.Link;` + "`" + ` but it should be ` + "`" + `type: DashboardLinkType` + "`" + `"},"url":{"description":"Link URL. Only required/valid if the type is link","type":"string"}},"required":["title","type","icon","tooltip","tags","asDropdown","targetBlank","includeVars","keepTime"],"type":"object"},"DashboardLinkPlacement":{"const":"inControlsMenu","description":"Dashboard Link placement. Defines where the link should be displayed.\n- \"inControlsMenu\" renders the link in bottom part of the dashboard controls dropdown menu","type":"string"},"DashboardLinkType":{"description":"Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)","enum":["link","dashboards"],"type":"string"},"DataLink":{"additionalProperties":false,"properties":{"targetBlank":{"type":"boolean"},"title":{"type":"string"},"url":{"type":"string"}},"required":["title","url"],"type":"object"},"DataQueryKind":{"additionalProperties":false,"properties":{"kind":{"description":"The kind of a DataQueryKind is the datasource type","type":"string"},"spec":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"}},"required":["kind","spec"],"type":"object"},"DataSourceRef":{"additionalProperties":false,"properties":{"type":{"description":"The plugin type-id","type":"string"},"uid":{"description":"Specific datasource instance","type":"string"}},"type":"object"},"DataTopic":{"description":"A topic is attached to DataFrame metadata in query results.\nThis specifies where the data should be used.","enum":["series","annotations","alertStates"],"type":"string"},"DataTransformerConfig":{"additionalProperties":false,"description":"Transformations allow to manipulate data returned by a query before the system applies a visualization.\nUsing transformations you can: rename fields, join time series data, perform mathematical operations across queries,\nuse the output of one transformation as the input to another transformation, etc.","properties":{"disabled":{"description":"Disabled transformations are skipped","type":"boolean"},"filter":{"$ref":"#/components/schemas/MatcherConfig","description":"Optional frame matcher. When missing it will be applied to all results"},"id":{"description":"Unique identifier of transformer","type":"string"},"options":{"additionalProperties":{},"description":"Options to be passed to the transformer\nValid options depend on the transformer id","type":"object"},"topic":{"$ref":"#/components/schemas/DataTopic","description":"Where to pull DataFrames from as input to transformation"}},"required":["id","options"],"type":"object"},"DatasourceVariableKind":{"additionalProperties":false,"description":"Datasource variable kind","properties":{"kind":{"const":"DatasourceVariable","type":"string"},"spec":{"$ref":"#/components/schemas/DatasourceVariableSpec"}},"required":["kind","spec"],"type":"object"},"DatasourceVariableSpec":{"additionalProperties":false,"description":"Datasource variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"pluginId":{"default":"","type":"string"},"refresh":{"$ref":"#/components/schemas/VariableRefresh","default":"never"},"regex":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","pluginId","refresh","regex","current","options","multi","includeAll","hide","skipUrlSync","allowCustomValue"],"type":"object"},"DynamicConfigValue":{"additionalProperties":false,"properties":{"id":{"default":"","type":"string"},"value":{"additionalProperties":{},"type":"object"}},"required":["id"],"type":"object"},"Element":{"description":"Supported dashboard elements\n|* more element types in the future","oneOf":[{"$ref":"#/components/schemas/PanelKind"},{"$ref":"#/components/schemas/LibraryPanelKind"}]},"ElementReference":{"additionalProperties":false,"properties":{"kind":{"const":"ElementReference","type":"string"},"name":{"type":"string"}},"required":["kind","name"],"type":"object"},"FetchOptions":{"additionalProperties":false,"properties":{"body":{"type":"string"},"headers":{"items":{"items":{"type":"string"},"type":"array"},"type":"array"},"method":{"$ref":"#/components/schemas/HttpRequestMethod"},"queryParams":{"description":"These are 2D arrays of strings, each representing a key-value pair\nWe are defining them this way because we can't generate a go struct that\nthat would have exactly two strings in each sub-array","items":{"items":{"type":"string"},"type":"array"},"type":"array"},"url":{"type":"string"}},"required":["method","url"],"type":"object"},"FieldColor":{"additionalProperties":false,"description":"Map a field to a color.","properties":{"fixedColor":{"description":"The fixed color value for fixed or shades color modes.","type":"string"},"mode":{"$ref":"#/components/schemas/FieldColorModeId","description":"The main color scheme mode."},"seriesBy":{"$ref":"#/components/schemas/FieldColorSeriesByMode","description":"Some visualizations need to know how to assign a series color from by value color schemes."}},"required":["mode"],"type":"object"},"FieldColorModeId":{"description":"Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value.\nContinuous color interpolates a color using the percentage of a value relative to min and max.\nAccepted values are:\n` + "`" + `thresholds` + "`" + `: From thresholds. Informs Grafana to take the color from the matching threshold\n` + "`" + `palette-classic` + "`" + `: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations\n` + "`" + `palette-classic-by-name` + "`" + `: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations\n` + "`" + `continuous-viridis` + "`" + `: Continuous Viridis palette mode\n` + "`" + `continuous-magma` + "`" + `: Continuous Magma palette mode\n` + "`" + `continuous-plasma` + "`" + `: Continuous Plasma palette mode\n` + "`" + `continuous-inferno` + "`" + `: Continuous Inferno palette mode\n` + "`" + `continuous-cividis` + "`" + `: Continuous Cividis palette mode\n` + "`" + `continuous-GrYlRd` + "`" + `: Continuous Green-Yellow-Red palette mode\n` + "`" + `continuous-RdYlGr` + "`" + `: Continuous Red-Yellow-Green palette mode\n` + "`" + `continuous-BlYlRd` + "`" + `: Continuous Blue-Yellow-Red palette mode\n` + "`" + `continuous-YlRd` + "`" + `: Continuous Yellow-Red palette mode\n` + "`" + `continuous-BlPu` + "`" + `: Continuous Blue-Purple palette mode\n` + "`" + `continuous-YlBl` + "`" + `: Continuous Yellow-Blue palette mode\n` + "`" + `continuous-blues` + "`" + `: Continuous Blue palette mode\n` + "`" + `continuous-reds` + "`" + `: Continuous Red palette mode\n` + "`" + `continuous-greens` + "`" + `: Continuous Green palette mode\n` + "`" + `continuous-purples` + "`" + `: Continuous Purple palette mode\n` + "`" + `shades` + "`" + `: Shades of a single color. Specify a single color, useful in an override rule.\n` + "`" + `fixed` + "`" + `: Fixed color mode. Specify a single color, useful in an override rule.","enum":["thresholds","palette-classic","palette-classic-by-name","continuous-viridis","continuous-magma","continuous-plasma","continuous-inferno","continuous-cividis","continuous-GrYlRd","continuous-RdYlGr","continuous-BlYlRd","continuous-YlRd","continuous-BlPu","continuous-YlBl","continuous-blues","continuous-reds","continuous-greens","continuous-purples","fixed","shades"],"type":"string"},"FieldColorSeriesByMode":{"description":"Defines how to assign a series color from \"by value\" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value.","enum":["min","max","last"],"type":"string"},"FieldConfig":{"additionalProperties":false,"description":"The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.\nEach column within this structure is called a field. A field can represent a single time series or table column.\nField options allow you to change how the data is displayed in your visualizations.","properties":{"actions":{"description":"Define interactive HTTP requests that can be triggered from data visualizations.","items":{"$ref":"#/components/schemas/Action"},"type":"array"},"color":{"$ref":"#/components/schemas/FieldColor","description":"Panel color configuration"},"custom":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"custom is specified by the FieldConfig field\nin panel plugin schemas.","type":"object"},"decimals":{"description":"Specify the number of decimals Grafana includes in the rendered value.\nIf you leave this field blank, Grafana automatically truncates the number of decimals based on the value.\nFor example 1.1234 will display as 1.12 and 100.456 will display as 100.\nTo display all decimals, set the unit to ` + "`" + `String` + "`" + `.","type":"number"},"description":{"description":"Human readable field metadata","type":"string"},"displayName":{"description":"The display value for this field. This supports template variables blank is auto","type":"string"},"displayNameFromDS":{"description":"This can be used by data sources that return and explicit naming structure for values and labels\nWhen this property is configured, this value is used rather than the default naming strategy.","type":"string"},"filterable":{"description":"True if data source field supports ad-hoc filters","type":"boolean"},"links":{"description":"The behavior when clicking on a result","items":{"additionalProperties":{},"type":"object"},"type":"array"},"mappings":{"description":"Convert input values into a display string","items":{"$ref":"#/components/schemas/ValueMapping"},"type":"array"},"max":{"description":"The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.","type":"number"},"min":{"description":"The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.","type":"number"},"noValue":{"description":"Alternative to empty string","type":"string"},"path":{"description":"An explicit path to the field in the datasource. When the frame meta includes a path,\nThis will default to ` + "`" + `${frame.meta.path}/${field.name}\n\nWhen defined, this value can be used as an identifier within the datasource scope, and\nmay be used to update the results","type":"string"},"thresholds":{"$ref":"#/components/schemas/ThresholdsConfig","description":"Map numeric values to states"},"unit":{"description":"Unit a field should use. The unit you select is applied to all fields except time.\nYou can use the units ID availables in Grafana or a custom unit.\nAvailable units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts\nAs custom unit, you can use the following formats:\n` + "`" + `suffix:\u003csuffix\u003e` + "`" + ` for custom unit that should go after value.\n` + "`" + `prefix:\u003cprefix\u003e` + "`" + ` for custom unit that should go before value.\n` + "`" + `time:\u003cformat\u003e` + "`" + ` For custom date time formats type for example ` + "`" + `time:YYYY-MM-DD` + "`" + `.\n` + "`" + `si:\u003cbase scale\u003e\u003cunit characters\u003e` + "`" + ` for custom SI units. For example: ` + "`" + `si: mF` + "`" + `. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character.\n` + "`" + `count:\u003cunit\u003e` + "`" + ` for a custom count unit.\n` + "`" + `currency:\u003cunit\u003e` + "`" + ` for custom a currency unit.","type":"string"},"writeable":{"description":"True if data source can write a value to the path. Auth/authz are supported separately","type":"boolean"}},"type":"object"},"FieldConfigSource":{"additionalProperties":false,"description":"The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.\nEach column within this structure is called a field. A field can represent a single time series or table column.\nField options allow you to change how the data is displayed in your visualizations.","properties":{"defaults":{"$ref":"#/components/schemas/FieldConfig","description":"Defaults are the options applied to all fields."},"overrides":{"description":"Overrides are the options applied to specific fields overriding the defaults.","items":{"additionalProperties":false,"properties":{"__systemRef":{"description":"Describes config override rules created when interacting with Grafana.","type":"string"},"matcher":{"$ref":"#/components/schemas/MatcherConfig"},"properties":{"items":{"$ref":"#/components/schemas/DynamicConfigValue"},"type":"array"}},"required":["matcher","properties"],"type":"object"},"type":"array"}},"required":["defaults","overrides"],"type":"object"},"FilterOrigin":{"const":"dashboard","description":"Determine the origin of the adhoc variable filter","type":"string"},"GridLayoutItemKind":{"additionalProperties":false,"properties":{"kind":{"const":"GridLayoutItem","type":"string"},"spec":{"$ref":"#/components/schemas/GridLayoutItemSpec"}},"required":["kind","spec"],"type":"object"},"GridLayoutItemSpec":{"additionalProperties":false,"properties":{"element":{"$ref":"#/components/schemas/ElementReference","description":"reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference"},"height":{"type":"integer"},"repeat":{"$ref":"#/components/schemas/RepeatOptions"},"width":{"type":"integer"},"x":{"type":"integer"},"y":{"type":"integer"}},"required":["x","y","width","height","element"],"type":"object"},"GridLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"GridLayout","type":"string"},"spec":{"$ref":"#/components/schemas/GridLayoutSpec"}},"required":["kind","spec"],"type":"object"},"GridLayoutSpec":{"additionalProperties":false,"properties":{"items":{"items":{"$ref":"#/components/schemas/GridLayoutItemKind"},"type":"array"}},"required":["items"],"type":"object"},"GroupByVariableKind":{"additionalProperties":false,"description":"Group variable kind","properties":{"kind":{"const":"GroupByVariable","type":"string"},"spec":{"$ref":"#/components/schemas/GroupByVariableSpec"}},"required":["kind","spec"],"type":"object"},"GroupByVariableSpec":{"additionalProperties":false,"description":"GroupBy variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"datasource":{"$ref":"#/components/schemas/DataSourceRef"},"defaultValue":{"$ref":"#/components/schemas/VariableOption"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","options","multi","hide","skipUrlSync"],"type":"object"},"HttpRequestMethod":{"enum":["GET","PUT","POST","DELETE","PATCH"],"type":"string"},"InfinityOptions":{"additionalProperties":false,"properties":{"body":{"type":"string"},"datasourceUid":{"type":"string"},"headers":{"items":{"items":{"type":"string"},"type":"array"},"type":"array"},"method":{"$ref":"#/components/schemas/HttpRequestMethod"},"queryParams":{"description":"These are 2D arrays of strings, each representing a key-value pair\nWe are defining them this way because we can't generate a go struct that\nthat would have exactly two strings in each sub-array","items":{"items":{"type":"string"},"type":"array"},"type":"array"},"url":{"type":"string"}},"required":["method","url","datasourceUid"],"type":"object"},"IntervalVariableKind":{"additionalProperties":false,"description":"Interval variable kind","properties":{"kind":{"const":"IntervalVariable","type":"string"},"spec":{"$ref":"#/components/schemas/IntervalVariableSpec"}},"required":["kind","spec"],"type":"object"},"IntervalVariableSpec":{"additionalProperties":false,"description":"Interval variable specification","properties":{"auto":{"default":false,"type":"boolean"},"auto_count":{"default":0,"type":"integer"},"auto_min":{"default":"","type":"string"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"query":{"default":"","type":"string"},"refresh":{"$ref":"#/components/schemas/VariableRefresh","default":"never"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","query","current","options","auto","auto_min","auto_count","refresh","hide","skipUrlSync"],"type":"object"},"LibraryPanelKind":{"additionalProperties":false,"properties":{"kind":{"const":"LibraryPanel","type":"string"},"spec":{"$ref":"#/components/schemas/LibraryPanelKindSpec"}},"required":["kind","spec"],"type":"object"},"LibraryPanelKindSpec":{"additionalProperties":false,"properties":{"id":{"description":"Panel ID for the library panel in the dashboard","type":"number"},"libraryPanel":{"$ref":"#/components/schemas/LibraryPanelRef"},"title":{"description":"Title for the library panel in the dashboard","type":"string"}},"required":["id","title","libraryPanel"],"type":"object"},"LibraryPanelRef":{"additionalProperties":false,"description":"A library panel is a reusable panel that you can use in any dashboard.\nWhen you make a change to a library panel, that change propagates to all instances of where the panel is used.\nLibrary panels streamline reuse of panels across multiple dashboards.","properties":{"name":{"description":"Library panel name","type":"string"},"uid":{"description":"Library panel uid","type":"string"}},"required":["name","uid"],"type":"object"},"MappingType":{"description":"Supported value mapping types\n` + "`" + `value` + "`" + `: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.\n` + "`" + `range` + "`" + `: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.\n` + "`" + `regex` + "`" + `: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.\n` + "`" + `special` + "`" + `: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A.","enum":["value","range","regex","special"],"type":"string"},"MatcherConfig":{"additionalProperties":false,"description":"Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation.\nIt comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type.","properties":{"id":{"default":"","description":"The matcher id. This is used to find the matcher implementation from registry.","type":"string"},"options":{"additionalProperties":{},"description":"The matcher options. This is specific to the matcher implementation.","type":"object"}},"required":["id"],"type":"object"},"MetricFindValue":{"additionalProperties":false,"description":"Define the MetricFindValue type","properties":{"expandable":{"type":"boolean"},"group":{"type":"string"},"text":{"type":"string"},"value":{"oneOf":[{"type":"string"},{"type":"number"}]}},"required":["text"],"type":"object"},"PanelKind":{"additionalProperties":false,"properties":{"kind":{"const":"Panel","type":"string"},"spec":{"$ref":"#/components/schemas/PanelSpec"}},"required":["kind","spec"],"type":"object"},"PanelQueryKind":{"additionalProperties":false,"properties":{"kind":{"const":"PanelQuery","type":"string"},"spec":{"$ref":"#/components/schemas/PanelQuerySpec"}},"required":["kind","spec"],"type":"object"},"PanelQuerySpec":{"additionalProperties":false,"properties":{"datasource":{"$ref":"#/components/schemas/DataSourceRef"},"hidden":{"type":"boolean"},"query":{"$ref":"#/components/schemas/DataQueryKind"},"refId":{"type":"string"}},"required":["query","refId","hidden"],"type":"object"},"PanelSpec":{"additionalProperties":false,"properties":{"data":{"$ref":"#/components/schemas/QueryGroupKind"},"description":{"type":"string"},"id":{"type":"number"},"links":{"items":{"$ref":"#/components/schemas/DataLink"},"type":"array"},"title":{"type":"string"},"transparent":{"type":"boolean"},"vizConfig":{"$ref":"#/components/schemas/VizConfigKind"}},"required":["id","title","description","links","data","vizConfig"],"type":"object"},"QueryGroupKind":{"additionalProperties":false,"properties":{"kind":{"const":"QueryGroup","type":"string"},"spec":{"$ref":"#/components/schemas/QueryGroupSpec"}},"required":["kind","spec"],"type":"object"},"QueryGroupSpec":{"additionalProperties":false,"properties":{"queries":{"items":{"$ref":"#/components/schemas/PanelQueryKind"},"type":"array"},"queryOptions":{"$ref":"#/components/schemas/QueryOptionsSpec"},"transformations":{"items":{"$ref":"#/components/schemas/TransformationKind"},"type":"array"}},"required":["queries","transformations","queryOptions"],"type":"object"},"QueryOptionsSpec":{"additionalProperties":false,"properties":{"cacheTimeout":{"type":"string"},"hideTimeOverride":{"type":"boolean"},"interval":{"type":"string"},"maxDataPoints":{"type":"integer"},"queryCachingTTL":{"type":"integer"},"timeFrom":{"type":"string"},"timeShift":{"type":"string"}},"type":"object"},"QueryVariableKind":{"additionalProperties":false,"description":"Query variable kind","properties":{"kind":{"const":"QueryVariable","type":"string"},"spec":{"$ref":"#/components/schemas/QueryVariableSpec"}},"required":["kind","spec"],"type":"object"},"QueryVariableSpec":{"additionalProperties":false,"description":"Query variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"datasource":{"$ref":"#/components/schemas/DataSourceRef"},"definition":{"type":"string"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"placeholder":{"type":"string"},"query":{"$ref":"#/components/schemas/DataQueryKind"},"refresh":{"$ref":"#/components/schemas/VariableRefresh","default":"never"},"regex":{"default":"","type":"string"},"regexApplyTo":{"$ref":"#/components/schemas/VariableRegexApplyTo","default":"value"},"skipUrlSync":{"default":false,"type":"boolean"},"sort":{"$ref":"#/components/schemas/VariableSort"},"staticOptions":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"staticOptionsOrder":{"enum":["before","after","sorted"],"type":"string"}},"required":["name","current","hide","refresh","skipUrlSync","query","regex","sort","options","multi","includeAll","allowCustomValue"],"type":"object"},"RangeMap":{"additionalProperties":false,"description":"Maps numerical ranges to a display text and color.\nFor example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.","properties":{"options":{"additionalProperties":false,"description":"Range to match against and the result to apply when the value is within the range","properties":{"from":{"description":"Min value of the range. It can be null which means -Infinity","type":"number"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value is within the range"},"to":{"description":"Max value of the range. It can be null which means +Infinity","type":"number"}},"required":["from","to","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"RegexMap":{"additionalProperties":false,"description":"Maps regular expressions to replacement text and a color.\nFor example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.","properties":{"options":{"additionalProperties":false,"description":"Regular expression to match against and the result to apply when the value matches the regex","properties":{"pattern":{"description":"Regular expression to match against","type":"string"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value matches the regex"}},"required":["pattern","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"RepeatMode":{"const":"variable","description":"other repeat modes will be added in the future: label, frame","type":"string"},"RepeatOptions":{"additionalProperties":false,"properties":{"direction":{"enum":["h","v"],"type":"string"},"maxPerRow":{"type":"integer"},"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"RowRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"RowsLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"RowsLayout","type":"string"},"spec":{"$ref":"#/components/schemas/RowsLayoutSpec"}},"required":["kind","spec"],"type":"object"},"RowsLayoutRowKind":{"additionalProperties":false,"properties":{"kind":{"const":"RowsLayoutRow","type":"string"},"spec":{"$ref":"#/components/schemas/RowsLayoutRowSpec"}},"required":["kind","spec"],"type":"object"},"RowsLayoutRowSpec":{"additionalProperties":false,"properties":{"collapse":{"type":"boolean"},"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"fillScreen":{"type":"boolean"},"hideHeader":{"type":"boolean"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"}]},"repeat":{"$ref":"#/components/schemas/RowRepeatOptions"},"title":{"type":"string"}},"required":["layout"],"type":"object"},"RowsLayoutSpec":{"additionalProperties":false,"properties":{"rows":{"items":{"$ref":"#/components/schemas/RowsLayoutRowKind"},"type":"array"}},"required":["rows"],"type":"object"},"SpecialValueMap":{"additionalProperties":false,"description":"Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color.\nSee SpecialValueMatch to see the list of special values.\nFor example, you can configure a special value mapping so that null values appear as N/A.","properties":{"options":{"additionalProperties":false,"properties":{"match":{"$ref":"#/components/schemas/SpecialValueMatch","description":"Special value to match against"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value matches the special value"}},"required":["match","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"SpecialValueMatch":{"description":"Special value types supported by the ` + "`" + `SpecialValueMap` + "`" + `","enum":["true","false","null","nan","null+nan","empty"],"type":"string"},"SwitchVariableKind":{"additionalProperties":false,"properties":{"kind":{"const":"SwitchVariable","type":"string"},"spec":{"$ref":"#/components/schemas/SwitchVariableSpec"}},"required":["kind","spec"],"type":"object"},"SwitchVariableSpec":{"additionalProperties":false,"description":"Switch variable specification","properties":{"current":{"default":"false","type":"string"},"description":{"type":"string"},"disabledValue":{"default":"false","type":"string"},"enabledValue":{"default":"true","type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","enabledValue","disabledValue","hide","skipUrlSync"],"type":"object"},"TabRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"TabsLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"TabsLayout","type":"string"},"spec":{"$ref":"#/components/schemas/TabsLayoutSpec"}},"required":["kind","spec"],"type":"object"},"TabsLayoutSpec":{"additionalProperties":false,"properties":{"tabs":{"items":{"$ref":"#/components/schemas/TabsLayoutTabKind"},"type":"array"}},"required":["tabs"],"type":"object"},"TabsLayoutTabKind":{"additionalProperties":false,"properties":{"kind":{"const":"TabsLayoutTab","type":"string"},"spec":{"$ref":"#/components/schemas/TabsLayoutTabSpec"}},"required":["kind","spec"],"type":"object"},"TabsLayoutTabSpec":{"additionalProperties":false,"properties":{"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"}]},"repeat":{"$ref":"#/components/schemas/TabRepeatOptions"},"title":{"type":"string"}},"required":["layout"],"type":"object"},"TextVariableKind":{"additionalProperties":false,"description":"Text variable kind","properties":{"kind":{"const":"TextVariable","type":"string"},"spec":{"$ref":"#/components/schemas/TextVariableSpec"}},"required":["kind","spec"],"type":"object"},"TextVariableSpec":{"additionalProperties":false,"description":"Text variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","query","hide","skipUrlSync"],"type":"object"},"Threshold":{"additionalProperties":false,"properties":{"color":{"type":"string"},"value":{"description":"Value null means -Infinity","type":"number"}},"required":["value","color"],"type":"object"},"ThresholdsConfig":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/ThresholdsMode"},"steps":{"items":{"$ref":"#/components/schemas/Threshold"},"type":"array"}},"required":["mode","steps"],"type":"object"},"ThresholdsMode":{"enum":["absolute","percentage"],"type":"string"},"TimeRangeOption":{"additionalProperties":false,"properties":{"display":{"default":"Last 6 hours","type":"string"},"from":{"default":"now-6h","type":"string"},"to":{"default":"now","type":"string"}},"required":["display","from","to"],"type":"object"},"TimeSettingsSpec":{"additionalProperties":false,"description":"Time configuration\nIt defines the default time config for the time picker, the refresh picker for the specific dashboard.","properties":{"autoRefresh":{"default":"","description":"Refresh rate of dashboard. Represented via interval string, e.g. \"5s\", \"1m\", \"1h\", \"1d\".\nv1: refresh","type":"string"},"autoRefreshIntervals":{"default":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"description":"Interval options available in the refresh picker dropdown.\nv1: timepicker.refresh_intervals","items":{"type":"string"},"type":"array"},"fiscalYearStartMonth":{"default":0,"description":"The month that the fiscal year starts on. 0 = January, 11 = December","type":"integer"},"from":{"default":"now-6h","description":"Start time range for dashboard.\nAccepted values are relative time strings like \"now-6h\" or absolute time strings like \"2020-07-10T08:00:00.000Z\".","type":"string"},"hideTimepicker":{"default":false,"description":"Whether timepicker is visible or not.\nv1: timepicker.hidden","type":"boolean"},"nowDelay":{"description":"Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values.\nv1: timepicker.nowDelay","type":"string"},"quickRanges":{"description":"Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard.\nv1: timepicker.quick_ranges , not exposed in the UI","items":{"$ref":"#/components/schemas/TimeRangeOption"},"type":"array"},"timezone":{"default":"browser","description":"Timezone of dashboard. Accepted values are IANA TZDB zone ID or \"browser\" or \"utc\".","type":"string"},"to":{"default":"now","description":"End time range for dashboard.\nAccepted values are relative time strings like \"now-6h\" or absolute time strings like \"2020-07-10T08:00:00.000Z\".","type":"string"},"weekStart":{"description":"Day when the week starts. Expressed by the name of the day in lowercase, e.g. \"monday\".","enum":["saturday","monday","sunday"],"type":"string"}},"required":["from","to","autoRefresh","autoRefreshIntervals","hideTimepicker","fiscalYearStartMonth"],"type":"object"},"TransformationKind":{"additionalProperties":false,"properties":{"kind":{"description":"The kind of a TransformationKind is the transformation ID","type":"string"},"spec":{"$ref":"#/components/schemas/DataTransformerConfig"}},"required":["kind","spec"],"type":"object"},"ValueMap":{"additionalProperties":false,"description":"Maps text values to a color or different display text and color.\nFor example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.","properties":{"options":{"additionalProperties":{"$ref":"#/components/schemas/ValueMappingResult"},"description":"Map with \u003cvalue_to_match\u003e: ValueMappingResult. For example: { \"10\": { text: \"Perfection!\", color: \"green\" } }","type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"ValueMapping":{"oneOf":[{"$ref":"#/components/schemas/ValueMap"},{"$ref":"#/components/schemas/RangeMap"},{"$ref":"#/components/schemas/RegexMap"},{"$ref":"#/components/schemas/SpecialValueMap"}]},"ValueMappingResult":{"additionalProperties":false,"description":"Result used as replacement with text and color when the value matches","properties":{"color":{"description":"Text to use when the value matches","type":"string"},"icon":{"description":"Icon to display when the value matches. Only specific visualizations.","type":"string"},"index":{"description":"Position in the mapping array. Only used internally.","type":"integer"},"text":{"description":"Text to display when the value matches","type":"string"}},"type":"object"},"VariableHide":{"description":"Determine if the variable shows on dashboard\nAccepted values are ` + "`" + `dontHide` + "`" + ` (show label and value), ` + "`" + `hideLabel` + "`" + ` (show value only), ` + "`" + `hideVariable` + "`" + ` (show nothing).","enum":["dontHide","hideLabel","hideVariable"],"type":"string"},"VariableKind":{"oneOf":[{"$ref":"#/components/schemas/QueryVariableKind"},{"$ref":"#/components/schemas/TextVariableKind"},{"$ref":"#/components/schemas/ConstantVariableKind"},{"$ref":"#/components/schemas/DatasourceVariableKind"},{"$ref":"#/components/schemas/IntervalVariableKind"},{"$ref":"#/components/schemas/CustomVariableKind"},{"$ref":"#/components/schemas/GroupByVariableKind"},{"$ref":"#/components/schemas/AdhocVariableKind"},{"$ref":"#/components/schemas/SwitchVariableKind"}]},"VariableOption":{"additionalProperties":false,"description":"Variable option specification","properties":{"selected":{"description":"Whether the option is selected or not","type":"boolean"},"text":{"description":"Text to be displayed for the option","oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]},"value":{"description":"Value of the option","oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]}},"required":["text","value"],"type":"object"},"VariableRefresh":{"description":"Options to config when to refresh a variable\n` + "`" + `never` + "`" + `: Never refresh the variable\n` + "`" + `onDashboardLoad` + "`" + `: Queries the data source every time the dashboard loads.\n` + "`" + `onTimeRangeChanged` + "`" + `: Queries the data source when the dashboard time range changes.","enum":["never","onDashboardLoad","onTimeRangeChanged"],"type":"string"},"VariableRegexApplyTo":{"description":"Determine whether regex applies to variable value or display text\nAccepted values are ` + "`" + `value` + "`" + ` (apply to value used in queries) or ` + "`" + `text` + "`" + ` (apply to display text shown to users)","enum":["value","text"],"type":"string"},"VariableSort":{"description":"Sort variable options\nAccepted values are:\n` + "`" + `disabled` + "`" + `: No sorting\n` + "`" + `alphabeticalAsc` + "`" + `: Alphabetical ASC\n` + "`" + `alphabeticalDesc` + "`" + `: Alphabetical DESC\n` + "`" + `numericalAsc` + "`" + `: Numerical ASC\n` + "`" + `numericalDesc` + "`" + `: Numerical DESC\n` + "`" + `alphabeticalCaseInsensitiveAsc` + "`" + `: Alphabetical Case Insensitive ASC\n` + "`" + `alphabeticalCaseInsensitiveDesc` + "`" + `: Alphabetical Case Insensitive DESC\n` + "`" + `naturalAsc` + "`" + `: Natural ASC\n` + "`" + `naturalDesc` + "`" + `: Natural DESC\nVariableSort enum with default value","enum":["disabled","alphabeticalAsc","alphabeticalDesc","numericalAsc","numericalDesc","alphabeticalCaseInsensitiveAsc","alphabeticalCaseInsensitiveDesc","naturalAsc","naturalDesc"],"type":"string"},"VizConfigKind":{"additionalProperties":false,"properties":{"kind":{"description":"The kind of a VizConfigKind is the plugin ID","type":"string"},"spec":{"$ref":"#/components/schemas/VizConfigSpec"}},"required":["kind","spec"],"type":"object"},"VizConfigSpec":{"additionalProperties":false,"description":"--- Kinds ---","properties":{"fieldConfig":{"$ref":"#/components/schemas/FieldConfigSource"},"options":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"},"pluginVersion":{"type":"string"}},"required":["pluginVersion","options","fieldConfig"],"type":"object"},"spec":{"additionalProperties":false,"properties":{"annotations":{"items":{"$ref":"#/components/schemas/AnnotationQueryKind"},"type":"array"},"cursorSync":{"$ref":"#/components/schemas/DashboardCursorSync","default":"Off","description":"Configuration of dashboard cursor sync behavior.\n\"Off\" for no shared crosshair or tooltip (default).\n\"Crosshair\" for shared crosshair.\n\"Tooltip\" for shared crosshair AND shared tooltip."},"description":{"description":"Description of dashboard.","type":"string"},"editable":{"default":true,"description":"Whether a dashboard is editable or not.","type":"boolean"},"elements":{"additionalProperties":{"$ref":"#/components/schemas/Element"},"type":"object"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"}]},"links":{"description":"Links with references to other dashboards or external websites.","items":{"$ref":"#/components/schemas/DashboardLink"},"type":"array"},"liveNow":{"description":"When set to true, the dashboard will redraw panels at an interval matching the pixel width.\nThis will keep data \"moving left\" regardless of the query refresh rate. This setting helps\navoid dashboards presenting stale live data.","type":"boolean"},"preload":{"default":false,"description":"When set to true, the dashboard will load all panels in the dashboard when it's loaded.","type":"boolean"},"revision":{"description":"Plugins only. The version of the dashboard installed together with the plugin.\nThis is used to determine if the dashboard should be updated when the plugin is updated.","type":"integer"},"tags":{"description":"Tags associated with dashboard.","items":{"type":"string"},"type":"array"},"timeSettings":{"$ref":"#/components/schemas/TimeSettingsSpec"},"title":{"description":"Title of dashboard.","type":"string"},"variables":{"description":"Configured template variables.","items":{"$ref":"#/components/schemas/VariableKind"},"type":"array"}},"required":["annotations","cursorSync","elements","layout","links","preload","tags","timeSettings","title","variables"],"type":"object"},"status":{"additionalProperties":false,"properties":{"conversion":{"$ref":"#/components/schemas/ConversionStatus","description":"Optional conversion status."}},"type":"object"}}`)
+ rawSchemaDashboardv2alpha1 = []byte(`{"Action":{"additionalProperties":false,"properties":{"confirmation":{"type":"string"},"fetch":{"$ref":"#/components/schemas/FetchOptions"},"infinity":{"$ref":"#/components/schemas/InfinityOptions"},"oneClick":{"type":"boolean"},"style":{"additionalProperties":false,"properties":{"backgroundColor":{"type":"string"}},"type":"object"},"title":{"type":"string"},"type":{"$ref":"#/components/schemas/ActionType"},"variables":{"items":{"$ref":"#/components/schemas/ActionVariable"},"type":"array"}},"required":["type","title"],"type":"object"},"ActionType":{"enum":["fetch","infinity"],"type":"string"},"ActionVariable":{"additionalProperties":false,"properties":{"key":{"type":"string"},"name":{"type":"string"},"type":{"$ref":"#/components/schemas/ActionVariableType"}},"required":["key","name","type"],"type":"object"},"ActionVariableType":{"const":"string","description":"Action variable type","type":"string"},"AdHocFilterWithLabels":{"additionalProperties":false,"description":"Define the AdHocFilterWithLabels type","properties":{"condition":{"description":"@deprecated","type":"string"},"forceEdit":{"type":"boolean"},"key":{"type":"string"},"keyLabel":{"type":"string"},"operator":{"type":"string"},"origin":{"$ref":"#/components/schemas/FilterOrigin"},"value":{"type":"string"},"valueLabels":{"items":{"type":"string"},"type":"array"},"values":{"items":{"type":"string"},"type":"array"}},"required":["key","operator","value"],"type":"object"},"AdhocVariableKind":{"additionalProperties":false,"description":"Adhoc variable kind","properties":{"kind":{"const":"AdhocVariable","type":"string"},"spec":{"$ref":"#/components/schemas/AdhocVariableSpec"}},"required":["kind","spec"],"type":"object"},"AdhocVariableSpec":{"additionalProperties":false,"description":"Adhoc variable specification","properties":{"allowCustomValue":{"default":true,"type":"boolean"},"baseFilters":{"items":{"$ref":"#/components/schemas/AdHocFilterWithLabels"},"type":"array"},"datasource":{"$ref":"#/components/schemas/DataSourceRef"},"defaultKeys":{"items":{"$ref":"#/components/schemas/MetricFindValue"},"type":"array"},"description":{"type":"string"},"filters":{"items":{"$ref":"#/components/schemas/AdHocFilterWithLabels"},"type":"array"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","baseFilters","filters","defaultKeys","hide","skipUrlSync","allowCustomValue"],"type":"object"},"AnnotationEventFieldMapping":{"additionalProperties":false,"description":"Annotation event field mapping. Defines how to map a data frame field to an annotation event field.","properties":{"regex":{"description":"Regular expression to apply to the field value","type":"string"},"source":{"default":"field","description":"Source type for the field value","type":"string"},"value":{"description":"Constant value to use when source is \"text\"","type":"string"}},"type":"object"},"AnnotationPanelFilter":{"additionalProperties":false,"properties":{"exclude":{"default":false,"description":"Should the specified panels be included or excluded","type":"boolean"},"ids":{"description":"Panel IDs that should be included or excluded","items":{"type":"integer"},"type":"array"}},"required":["ids"],"type":"object"},"AnnotationQueryKind":{"additionalProperties":false,"properties":{"kind":{"const":"AnnotationQuery","type":"string"},"spec":{"$ref":"#/components/schemas/AnnotationQuerySpec"}},"required":["kind","spec"],"type":"object"},"AnnotationQuerySpec":{"additionalProperties":false,"properties":{"builtIn":{"default":false,"type":"boolean"},"datasource":{"$ref":"#/components/schemas/DataSourceRef"},"enable":{"type":"boolean"},"filter":{"$ref":"#/components/schemas/AnnotationPanelFilter"},"hide":{"type":"boolean"},"iconColor":{"type":"string"},"legacyOptions":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"Catch-all field for datasource-specific properties","type":"object"},"mappings":{"additionalProperties":{"$ref":"#/components/schemas/AnnotationEventFieldMapping"},"description":"Mappings define how to convert data frame fields to annotation event fields.","type":"object"},"name":{"type":"string"},"query":{"$ref":"#/components/schemas/DataQueryKind"}},"required":["enable","hide","iconColor","name"],"type":"object"},"AutoGridLayoutItemKind":{"additionalProperties":false,"properties":{"kind":{"const":"AutoGridLayoutItem","type":"string"},"spec":{"$ref":"#/components/schemas/AutoGridLayoutItemSpec"}},"required":["kind","spec"],"type":"object"},"AutoGridLayoutItemSpec":{"additionalProperties":false,"properties":{"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"element":{"$ref":"#/components/schemas/ElementReference"},"repeat":{"$ref":"#/components/schemas/AutoGridRepeatOptions"}},"required":["element"],"type":"object"},"AutoGridLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"AutoGridLayout","type":"string"},"spec":{"$ref":"#/components/schemas/AutoGridLayoutSpec"}},"required":["kind","spec"],"type":"object"},"AutoGridLayoutSpec":{"additionalProperties":false,"properties":{"columnWidth":{"type":"number"},"columnWidthMode":{"default":"standard","enum":["narrow","standard","wide","custom"],"type":"string"},"fillScreen":{"default":false,"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/AutoGridLayoutItemKind"},"type":"array"},"maxColumnCount":{"default":3,"type":"number"},"rowHeight":{"type":"number"},"rowHeightMode":{"default":"standard","enum":["short","standard","tall","custom"],"type":"string"}},"required":["columnWidthMode","rowHeightMode","items"],"type":"object"},"AutoGridRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"ConditionalRenderingDataKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingData","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingDataSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingDataSpec":{"additionalProperties":false,"properties":{"value":{"type":"boolean"}},"required":["value"],"type":"object"},"ConditionalRenderingGroupKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingGroup","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingGroupSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingGroupSpec":{"additionalProperties":false,"properties":{"condition":{"enum":["and","or"],"type":"string"},"items":{"items":{"oneOf":[{"$ref":"#/components/schemas/ConditionalRenderingVariableKind"},{"$ref":"#/components/schemas/ConditionalRenderingDataKind"},{"$ref":"#/components/schemas/ConditionalRenderingTimeRangeSizeKind"}]},"type":"array"},"visibility":{"enum":["show","hide"],"type":"string"}},"required":["visibility","condition","items"],"type":"object"},"ConditionalRenderingTimeRangeSizeKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingTimeRangeSize","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingTimeRangeSizeSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingTimeRangeSizeSpec":{"additionalProperties":false,"properties":{"value":{"type":"string"}},"required":["value"],"type":"object"},"ConditionalRenderingVariableKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingVariable","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingVariableSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingVariableSpec":{"additionalProperties":false,"properties":{"operator":{"enum":["equals","notEquals","matches","notMatches"],"type":"string"},"value":{"type":"string"},"variable":{"type":"string"}},"required":["variable","operator","value"],"type":"object"},"ConstantVariableKind":{"additionalProperties":false,"description":"Constant variable kind","properties":{"kind":{"const":"ConstantVariable","type":"string"},"spec":{"$ref":"#/components/schemas/ConstantVariableSpec"}},"required":["kind","spec"],"type":"object"},"ConstantVariableSpec":{"additionalProperties":false,"description":"Constant variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","query","current","hide","skipUrlSync"],"type":"object"},"ConversionStatus":{"additionalProperties":false,"description":"ConversionStatus is the status of the conversion of the dashboard.","properties":{"error":{"description":"The error message from the conversion.\nEmpty if the conversion has not failed.","type":"string"},"failed":{"description":"Whether from another version has failed.\nIf true, means that the dashboard is not valid,\nand the caller should instead fetch the stored version.","type":"boolean"},"source":{"additionalProperties":{},"description":"The original value map[string]any","type":"object"},"storedVersion":{"description":"The version which was stored when the dashboard was created / updated.\nFetching this version should always succeed.","type":"string"}},"required":["failed"],"type":"object"},"CustomVariableKind":{"additionalProperties":false,"description":"Custom variable kind","properties":{"kind":{"const":"CustomVariable","type":"string"},"spec":{"$ref":"#/components/schemas/CustomVariableSpec"}},"required":["kind","spec"],"type":"object"},"CustomVariableSpec":{"additionalProperties":false,"description":"Custom variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"},"valuesFormat":{"enum":["csv","json"],"type":"string"}},"required":["name","query","current","options","multi","includeAll","hide","skipUrlSync","allowCustomValue"],"type":"object"},"Dashboard":{"properties":{"spec":{"$ref":"#/components/schemas/spec"},"status":{"$ref":"#/components/schemas/status"}},"required":["spec"]},"DashboardCursorSync":{"description":"\"Off\" for no shared crosshair or tooltip (default).\n\"Crosshair\" for shared crosshair.\n\"Tooltip\" for shared crosshair AND shared tooltip.","enum":["Crosshair","Tooltip","Off"],"type":"string"},"DashboardLink":{"additionalProperties":false,"description":"Links with references to other dashboards or external resources","properties":{"asDropdown":{"default":false,"description":"If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards","type":"boolean"},"icon":{"description":"Icon name to be displayed with the link","type":"string"},"includeVars":{"default":false,"description":"If true, includes current template variables values in the link as query params","type":"boolean"},"keepTime":{"default":false,"description":"If true, includes current time range in the link as query params","type":"boolean"},"placement":{"$ref":"#/components/schemas/DashboardLinkPlacement","description":"Placement can be used to display the link somewhere else on the dashboard other than above the visualisations."},"tags":{"description":"List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards","items":{"type":"string"},"type":"array"},"targetBlank":{"default":false,"description":"If true, the link will be opened in a new tab","type":"boolean"},"title":{"description":"Title to display with the link","type":"string"},"tooltip":{"description":"Tooltip to display when the user hovers their mouse over it","type":"string"},"type":{"$ref":"#/components/schemas/DashboardLinkType","description":"Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)\nFIXME: The type is generated as ` + "`" + `type: DashboardLinkType | dashboardLinkType.Link;` + "`" + ` but it should be ` + "`" + `type: DashboardLinkType` + "`" + `"},"url":{"description":"Link URL. Only required/valid if the type is link","type":"string"}},"required":["title","type","icon","tooltip","tags","asDropdown","targetBlank","includeVars","keepTime"],"type":"object"},"DashboardLinkPlacement":{"const":"inControlsMenu","description":"Dashboard Link placement. Defines where the link should be displayed.\n- \"inControlsMenu\" renders the link in bottom part of the dashboard controls dropdown menu","type":"string"},"DashboardLinkType":{"description":"Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)","enum":["link","dashboards"],"type":"string"},"DataLink":{"additionalProperties":false,"properties":{"targetBlank":{"type":"boolean"},"title":{"type":"string"},"url":{"type":"string"}},"required":["title","url"],"type":"object"},"DataQueryKind":{"additionalProperties":false,"properties":{"kind":{"description":"The kind of a DataQueryKind is the datasource type","type":"string"},"spec":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"}},"required":["kind","spec"],"type":"object"},"DataSourceRef":{"additionalProperties":false,"properties":{"type":{"description":"The plugin type-id","type":"string"},"uid":{"description":"Specific datasource instance","type":"string"}},"type":"object"},"DataTopic":{"description":"A topic is attached to DataFrame metadata in query results.\nThis specifies where the data should be used.","enum":["series","annotations","alertStates"],"type":"string"},"DataTransformerConfig":{"additionalProperties":false,"description":"Transformations allow to manipulate data returned by a query before the system applies a visualization.\nUsing transformations you can: rename fields, join time series data, perform mathematical operations across queries,\nuse the output of one transformation as the input to another transformation, etc.","properties":{"disabled":{"description":"Disabled transformations are skipped","type":"boolean"},"filter":{"$ref":"#/components/schemas/MatcherConfig","description":"Optional frame matcher. When missing it will be applied to all results"},"id":{"description":"Unique identifier of transformer","type":"string"},"options":{"additionalProperties":{},"description":"Options to be passed to the transformer\nValid options depend on the transformer id","type":"object"},"topic":{"$ref":"#/components/schemas/DataTopic","description":"Where to pull DataFrames from as input to transformation"}},"required":["id","options"],"type":"object"},"DatasourceVariableKind":{"additionalProperties":false,"description":"Datasource variable kind","properties":{"kind":{"const":"DatasourceVariable","type":"string"},"spec":{"$ref":"#/components/schemas/DatasourceVariableSpec"}},"required":["kind","spec"],"type":"object"},"DatasourceVariableSpec":{"additionalProperties":false,"description":"Datasource variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"pluginId":{"default":"","type":"string"},"refresh":{"$ref":"#/components/schemas/VariableRefresh","default":"never"},"regex":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","pluginId","refresh","regex","current","options","multi","includeAll","hide","skipUrlSync","allowCustomValue"],"type":"object"},"DynamicConfigValue":{"additionalProperties":false,"properties":{"id":{"default":"","type":"string"},"value":{"additionalProperties":{},"type":"object"}},"required":["id"],"type":"object"},"Element":{"description":"Supported dashboard elements\n|* more element types in the future","oneOf":[{"$ref":"#/components/schemas/PanelKind"},{"$ref":"#/components/schemas/LibraryPanelKind"}]},"ElementReference":{"additionalProperties":false,"properties":{"kind":{"const":"ElementReference","type":"string"},"name":{"type":"string"}},"required":["kind","name"],"type":"object"},"FetchOptions":{"additionalProperties":false,"properties":{"body":{"type":"string"},"headers":{"items":{"items":{"type":"string"},"type":"array"},"type":"array"},"method":{"$ref":"#/components/schemas/HttpRequestMethod"},"queryParams":{"description":"These are 2D arrays of strings, each representing a key-value pair\nWe are defining them this way because we can't generate a go struct that\nthat would have exactly two strings in each sub-array","items":{"items":{"type":"string"},"type":"array"},"type":"array"},"url":{"type":"string"}},"required":["method","url"],"type":"object"},"FieldColor":{"additionalProperties":false,"description":"Map a field to a color.","properties":{"fixedColor":{"description":"The fixed color value for fixed or shades color modes.","type":"string"},"mode":{"$ref":"#/components/schemas/FieldColorModeId","description":"The main color scheme mode."},"seriesBy":{"$ref":"#/components/schemas/FieldColorSeriesByMode","description":"Some visualizations need to know how to assign a series color from by value color schemes."}},"required":["mode"],"type":"object"},"FieldColorModeId":{"description":"Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value.\nContinuous color interpolates a color using the percentage of a value relative to min and max.\nAccepted values are:\n` + "`" + `thresholds` + "`" + `: From thresholds. Informs Grafana to take the color from the matching threshold\n` + "`" + `palette-classic` + "`" + `: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations\n` + "`" + `palette-classic-by-name` + "`" + `: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations\n` + "`" + `continuous-viridis` + "`" + `: Continuous Viridis palette mode\n` + "`" + `continuous-magma` + "`" + `: Continuous Magma palette mode\n` + "`" + `continuous-plasma` + "`" + `: Continuous Plasma palette mode\n` + "`" + `continuous-inferno` + "`" + `: Continuous Inferno palette mode\n` + "`" + `continuous-cividis` + "`" + `: Continuous Cividis palette mode\n` + "`" + `continuous-GrYlRd` + "`" + `: Continuous Green-Yellow-Red palette mode\n` + "`" + `continuous-RdYlGr` + "`" + `: Continuous Red-Yellow-Green palette mode\n` + "`" + `continuous-BlYlRd` + "`" + `: Continuous Blue-Yellow-Red palette mode\n` + "`" + `continuous-YlRd` + "`" + `: Continuous Yellow-Red palette mode\n` + "`" + `continuous-BlPu` + "`" + `: Continuous Blue-Purple palette mode\n` + "`" + `continuous-YlBl` + "`" + `: Continuous Yellow-Blue palette mode\n` + "`" + `continuous-blues` + "`" + `: Continuous Blue palette mode\n` + "`" + `continuous-reds` + "`" + `: Continuous Red palette mode\n` + "`" + `continuous-greens` + "`" + `: Continuous Green palette mode\n` + "`" + `continuous-purples` + "`" + `: Continuous Purple palette mode\n` + "`" + `shades` + "`" + `: Shades of a single color. Specify a single color, useful in an override rule.\n` + "`" + `fixed` + "`" + `: Fixed color mode. Specify a single color, useful in an override rule.","enum":["thresholds","palette-classic","palette-classic-by-name","continuous-viridis","continuous-magma","continuous-plasma","continuous-inferno","continuous-cividis","continuous-GrYlRd","continuous-RdYlGr","continuous-BlYlRd","continuous-YlRd","continuous-BlPu","continuous-YlBl","continuous-blues","continuous-reds","continuous-greens","continuous-purples","fixed","shades"],"type":"string"},"FieldColorSeriesByMode":{"description":"Defines how to assign a series color from \"by value\" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value.","enum":["min","max","last"],"type":"string"},"FieldConfig":{"additionalProperties":false,"description":"The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.\nEach column within this structure is called a field. A field can represent a single time series or table column.\nField options allow you to change how the data is displayed in your visualizations.","properties":{"actions":{"description":"Define interactive HTTP requests that can be triggered from data visualizations.","items":{"$ref":"#/components/schemas/Action"},"type":"array"},"color":{"$ref":"#/components/schemas/FieldColor","description":"Panel color configuration"},"custom":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"custom is specified by the FieldConfig field\nin panel plugin schemas.","type":"object"},"decimals":{"description":"Specify the number of decimals Grafana includes in the rendered value.\nIf you leave this field blank, Grafana automatically truncates the number of decimals based on the value.\nFor example 1.1234 will display as 1.12 and 100.456 will display as 100.\nTo display all decimals, set the unit to ` + "`" + `String` + "`" + `.","type":"number"},"description":{"description":"Human readable field metadata","type":"string"},"displayName":{"description":"The display value for this field. This supports template variables blank is auto","type":"string"},"displayNameFromDS":{"description":"This can be used by data sources that return and explicit naming structure for values and labels\nWhen this property is configured, this value is used rather than the default naming strategy.","type":"string"},"fieldMinMax":{"description":"Calculate min max per field","type":"boolean"},"filterable":{"description":"True if data source field supports ad-hoc filters","type":"boolean"},"links":{"description":"The behavior when clicking on a result","items":{"additionalProperties":{},"type":"object"},"type":"array"},"mappings":{"description":"Convert input values into a display string","items":{"$ref":"#/components/schemas/ValueMapping"},"type":"array"},"max":{"description":"The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.","type":"number"},"min":{"description":"The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.","type":"number"},"noValue":{"description":"Alternative to empty string","type":"string"},"nullValueMode":{"$ref":"#/components/schemas/NullValueMode","description":"How null values should be handled when calculating field stats\n\"null\" - Include null values, \"connected\" - Ignore nulls, \"null as zero\" - Treat nulls as zero"},"path":{"description":"An explicit path to the field in the datasource. When the frame meta includes a path,\nThis will default to ` + "`" + `${frame.meta.path}/${field.name}\n\nWhen defined, this value can be used as an identifier within the datasource scope, and\nmay be used to update the results","type":"string"},"thresholds":{"$ref":"#/components/schemas/ThresholdsConfig","description":"Map numeric values to states"},"unit":{"description":"Unit a field should use. The unit you select is applied to all fields except time.\nYou can use the units ID availables in Grafana or a custom unit.\nAvailable units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts\nAs custom unit, you can use the following formats:\n` + "`" + `suffix:\u003csuffix\u003e` + "`" + ` for custom unit that should go after value.\n` + "`" + `prefix:\u003cprefix\u003e` + "`" + ` for custom unit that should go before value.\n` + "`" + `time:\u003cformat\u003e` + "`" + ` For custom date time formats type for example ` + "`" + `time:YYYY-MM-DD` + "`" + `.\n` + "`" + `si:\u003cbase scale\u003e\u003cunit characters\u003e` + "`" + ` for custom SI units. For example: ` + "`" + `si: mF` + "`" + `. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character.\n` + "`" + `count:\u003cunit\u003e` + "`" + ` for a custom count unit.\n` + "`" + `currency:\u003cunit\u003e` + "`" + ` for custom a currency unit.","type":"string"},"writeable":{"description":"True if data source can write a value to the path. Auth/authz are supported separately","type":"boolean"}},"type":"object"},"FieldConfigSource":{"additionalProperties":false,"description":"The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.\nEach column within this structure is called a field. A field can represent a single time series or table column.\nField options allow you to change how the data is displayed in your visualizations.","properties":{"defaults":{"$ref":"#/components/schemas/FieldConfig","description":"Defaults are the options applied to all fields."},"overrides":{"description":"Overrides are the options applied to specific fields overriding the defaults.","items":{"additionalProperties":false,"properties":{"__systemRef":{"description":"Describes config override rules created when interacting with Grafana.","type":"string"},"matcher":{"$ref":"#/components/schemas/MatcherConfig"},"properties":{"items":{"$ref":"#/components/schemas/DynamicConfigValue"},"type":"array"}},"required":["matcher","properties"],"type":"object"},"type":"array"}},"required":["defaults","overrides"],"type":"object"},"FilterOrigin":{"const":"dashboard","description":"Determine the origin of the adhoc variable filter","type":"string"},"GridLayoutItemKind":{"additionalProperties":false,"properties":{"kind":{"const":"GridLayoutItem","type":"string"},"spec":{"$ref":"#/components/schemas/GridLayoutItemSpec"}},"required":["kind","spec"],"type":"object"},"GridLayoutItemSpec":{"additionalProperties":false,"properties":{"element":{"$ref":"#/components/schemas/ElementReference","description":"reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference"},"height":{"type":"integer"},"repeat":{"$ref":"#/components/schemas/RepeatOptions"},"width":{"type":"integer"},"x":{"type":"integer"},"y":{"type":"integer"}},"required":["x","y","width","height","element"],"type":"object"},"GridLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"GridLayout","type":"string"},"spec":{"$ref":"#/components/schemas/GridLayoutSpec"}},"required":["kind","spec"],"type":"object"},"GridLayoutSpec":{"additionalProperties":false,"properties":{"items":{"items":{"$ref":"#/components/schemas/GridLayoutItemKind"},"type":"array"}},"required":["items"],"type":"object"},"GroupByVariableKind":{"additionalProperties":false,"description":"Group variable kind","properties":{"kind":{"const":"GroupByVariable","type":"string"},"spec":{"$ref":"#/components/schemas/GroupByVariableSpec"}},"required":["kind","spec"],"type":"object"},"GroupByVariableSpec":{"additionalProperties":false,"description":"GroupBy variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"datasource":{"$ref":"#/components/schemas/DataSourceRef"},"defaultValue":{"$ref":"#/components/schemas/VariableOption"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","options","multi","hide","skipUrlSync"],"type":"object"},"HttpRequestMethod":{"enum":["GET","PUT","POST","DELETE","PATCH"],"type":"string"},"InfinityOptions":{"additionalProperties":false,"properties":{"body":{"type":"string"},"datasourceUid":{"type":"string"},"headers":{"items":{"items":{"type":"string"},"type":"array"},"type":"array"},"method":{"$ref":"#/components/schemas/HttpRequestMethod"},"queryParams":{"description":"These are 2D arrays of strings, each representing a key-value pair\nWe are defining them this way because we can't generate a go struct that\nthat would have exactly two strings in each sub-array","items":{"items":{"type":"string"},"type":"array"},"type":"array"},"url":{"type":"string"}},"required":["method","url","datasourceUid"],"type":"object"},"IntervalVariableKind":{"additionalProperties":false,"description":"Interval variable kind","properties":{"kind":{"const":"IntervalVariable","type":"string"},"spec":{"$ref":"#/components/schemas/IntervalVariableSpec"}},"required":["kind","spec"],"type":"object"},"IntervalVariableSpec":{"additionalProperties":false,"description":"Interval variable specification","properties":{"auto":{"default":false,"type":"boolean"},"auto_count":{"default":0,"type":"integer"},"auto_min":{"default":"","type":"string"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"query":{"default":"","type":"string"},"refresh":{"$ref":"#/components/schemas/VariableRefresh","default":"never"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","query","current","options","auto","auto_min","auto_count","refresh","hide","skipUrlSync"],"type":"object"},"LibraryPanelKind":{"additionalProperties":false,"properties":{"kind":{"const":"LibraryPanel","type":"string"},"spec":{"$ref":"#/components/schemas/LibraryPanelKindSpec"}},"required":["kind","spec"],"type":"object"},"LibraryPanelKindSpec":{"additionalProperties":false,"properties":{"id":{"description":"Panel ID for the library panel in the dashboard","type":"number"},"libraryPanel":{"$ref":"#/components/schemas/LibraryPanelRef"},"title":{"description":"Title for the library panel in the dashboard","type":"string"}},"required":["id","title","libraryPanel"],"type":"object"},"LibraryPanelRef":{"additionalProperties":false,"description":"A library panel is a reusable panel that you can use in any dashboard.\nWhen you make a change to a library panel, that change propagates to all instances of where the panel is used.\nLibrary panels streamline reuse of panels across multiple dashboards.","properties":{"name":{"description":"Library panel name","type":"string"},"uid":{"description":"Library panel uid","type":"string"}},"required":["name","uid"],"type":"object"},"MappingType":{"description":"Supported value mapping types\n` + "`" + `value` + "`" + `: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.\n` + "`" + `range` + "`" + `: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.\n` + "`" + `regex` + "`" + `: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.\n` + "`" + `special` + "`" + `: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A.","enum":["value","range","regex","special"],"type":"string"},"MatcherConfig":{"additionalProperties":false,"description":"Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation.\nIt comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type.","properties":{"id":{"default":"","description":"The matcher id. This is used to find the matcher implementation from registry.","type":"string"},"options":{"additionalProperties":{},"description":"The matcher options. This is specific to the matcher implementation.","type":"object"}},"required":["id"],"type":"object"},"MetricFindValue":{"additionalProperties":false,"description":"Define the MetricFindValue type","properties":{"expandable":{"type":"boolean"},"group":{"type":"string"},"text":{"type":"string"},"value":{"oneOf":[{"type":"string"},{"type":"number"}]}},"required":["text"],"type":"object"},"NullValueMode":{"description":"How null values should be handled","enum":["null","connected","null as zero"],"type":"string"},"PanelKind":{"additionalProperties":false,"properties":{"kind":{"const":"Panel","type":"string"},"spec":{"$ref":"#/components/schemas/PanelSpec"}},"required":["kind","spec"],"type":"object"},"PanelQueryKind":{"additionalProperties":false,"properties":{"kind":{"const":"PanelQuery","type":"string"},"spec":{"$ref":"#/components/schemas/PanelQuerySpec"}},"required":["kind","spec"],"type":"object"},"PanelQuerySpec":{"additionalProperties":false,"properties":{"datasource":{"$ref":"#/components/schemas/DataSourceRef"},"hidden":{"type":"boolean"},"query":{"$ref":"#/components/schemas/DataQueryKind"},"refId":{"type":"string"}},"required":["query","refId","hidden"],"type":"object"},"PanelSpec":{"additionalProperties":false,"properties":{"data":{"$ref":"#/components/schemas/QueryGroupKind"},"description":{"type":"string"},"id":{"type":"number"},"links":{"items":{"$ref":"#/components/schemas/DataLink"},"type":"array"},"title":{"type":"string"},"transparent":{"type":"boolean"},"vizConfig":{"$ref":"#/components/schemas/VizConfigKind"}},"required":["id","title","description","links","data","vizConfig"],"type":"object"},"QueryGroupKind":{"additionalProperties":false,"properties":{"kind":{"const":"QueryGroup","type":"string"},"spec":{"$ref":"#/components/schemas/QueryGroupSpec"}},"required":["kind","spec"],"type":"object"},"QueryGroupSpec":{"additionalProperties":false,"properties":{"queries":{"items":{"$ref":"#/components/schemas/PanelQueryKind"},"type":"array"},"queryOptions":{"$ref":"#/components/schemas/QueryOptionsSpec"},"transformations":{"items":{"$ref":"#/components/schemas/TransformationKind"},"type":"array"}},"required":["queries","transformations","queryOptions"],"type":"object"},"QueryOptionsSpec":{"additionalProperties":false,"properties":{"cacheTimeout":{"type":"string"},"hideTimeOverride":{"type":"boolean"},"interval":{"type":"string"},"maxDataPoints":{"type":"integer"},"queryCachingTTL":{"type":"integer"},"timeFrom":{"type":"string"},"timeShift":{"type":"string"}},"type":"object"},"QueryVariableKind":{"additionalProperties":false,"description":"Query variable kind","properties":{"kind":{"const":"QueryVariable","type":"string"},"spec":{"$ref":"#/components/schemas/QueryVariableSpec"}},"required":["kind","spec"],"type":"object"},"QueryVariableSpec":{"additionalProperties":false,"description":"Query variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"datasource":{"$ref":"#/components/schemas/DataSourceRef"},"definition":{"type":"string"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"placeholder":{"type":"string"},"query":{"$ref":"#/components/schemas/DataQueryKind"},"refresh":{"$ref":"#/components/schemas/VariableRefresh","default":"never"},"regex":{"default":"","type":"string"},"regexApplyTo":{"$ref":"#/components/schemas/VariableRegexApplyTo","default":"value"},"skipUrlSync":{"default":false,"type":"boolean"},"sort":{"$ref":"#/components/schemas/VariableSort"},"staticOptions":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"staticOptionsOrder":{"enum":["before","after","sorted"],"type":"string"}},"required":["name","current","hide","refresh","skipUrlSync","query","regex","sort","options","multi","includeAll","allowCustomValue"],"type":"object"},"RangeMap":{"additionalProperties":false,"description":"Maps numerical ranges to a display text and color.\nFor example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.","properties":{"options":{"additionalProperties":false,"description":"Range to match against and the result to apply when the value is within the range","properties":{"from":{"description":"Min value of the range. It can be null which means -Infinity","type":"number"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value is within the range"},"to":{"description":"Max value of the range. It can be null which means +Infinity","type":"number"}},"required":["from","to","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"RegexMap":{"additionalProperties":false,"description":"Maps regular expressions to replacement text and a color.\nFor example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.","properties":{"options":{"additionalProperties":false,"description":"Regular expression to match against and the result to apply when the value matches the regex","properties":{"pattern":{"description":"Regular expression to match against","type":"string"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value matches the regex"}},"required":["pattern","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"RepeatMode":{"const":"variable","description":"other repeat modes will be added in the future: label, frame","type":"string"},"RepeatOptions":{"additionalProperties":false,"properties":{"direction":{"enum":["h","v"],"type":"string"},"maxPerRow":{"type":"integer"},"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"RowRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"RowsLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"RowsLayout","type":"string"},"spec":{"$ref":"#/components/schemas/RowsLayoutSpec"}},"required":["kind","spec"],"type":"object"},"RowsLayoutRowKind":{"additionalProperties":false,"properties":{"kind":{"const":"RowsLayoutRow","type":"string"},"spec":{"$ref":"#/components/schemas/RowsLayoutRowSpec"}},"required":["kind","spec"],"type":"object"},"RowsLayoutRowSpec":{"additionalProperties":false,"properties":{"collapse":{"type":"boolean"},"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"fillScreen":{"type":"boolean"},"hideHeader":{"type":"boolean"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"}]},"repeat":{"$ref":"#/components/schemas/RowRepeatOptions"},"title":{"type":"string"}},"required":["layout"],"type":"object"},"RowsLayoutSpec":{"additionalProperties":false,"properties":{"rows":{"items":{"$ref":"#/components/schemas/RowsLayoutRowKind"},"type":"array"}},"required":["rows"],"type":"object"},"SpecialValueMap":{"additionalProperties":false,"description":"Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color.\nSee SpecialValueMatch to see the list of special values.\nFor example, you can configure a special value mapping so that null values appear as N/A.","properties":{"options":{"additionalProperties":false,"properties":{"match":{"$ref":"#/components/schemas/SpecialValueMatch","description":"Special value to match against"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value matches the special value"}},"required":["match","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"SpecialValueMatch":{"description":"Special value types supported by the ` + "`" + `SpecialValueMap` + "`" + `","enum":["true","false","null","nan","null+nan","empty"],"type":"string"},"SwitchVariableKind":{"additionalProperties":false,"properties":{"kind":{"const":"SwitchVariable","type":"string"},"spec":{"$ref":"#/components/schemas/SwitchVariableSpec"}},"required":["kind","spec"],"type":"object"},"SwitchVariableSpec":{"additionalProperties":false,"description":"Switch variable specification","properties":{"current":{"default":"false","type":"string"},"description":{"type":"string"},"disabledValue":{"default":"false","type":"string"},"enabledValue":{"default":"true","type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","enabledValue","disabledValue","hide","skipUrlSync"],"type":"object"},"TabRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"TabsLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"TabsLayout","type":"string"},"spec":{"$ref":"#/components/schemas/TabsLayoutSpec"}},"required":["kind","spec"],"type":"object"},"TabsLayoutSpec":{"additionalProperties":false,"properties":{"tabs":{"items":{"$ref":"#/components/schemas/TabsLayoutTabKind"},"type":"array"}},"required":["tabs"],"type":"object"},"TabsLayoutTabKind":{"additionalProperties":false,"properties":{"kind":{"const":"TabsLayoutTab","type":"string"},"spec":{"$ref":"#/components/schemas/TabsLayoutTabSpec"}},"required":["kind","spec"],"type":"object"},"TabsLayoutTabSpec":{"additionalProperties":false,"properties":{"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"}]},"repeat":{"$ref":"#/components/schemas/TabRepeatOptions"},"title":{"type":"string"}},"required":["layout"],"type":"object"},"TextVariableKind":{"additionalProperties":false,"description":"Text variable kind","properties":{"kind":{"const":"TextVariable","type":"string"},"spec":{"$ref":"#/components/schemas/TextVariableSpec"}},"required":["kind","spec"],"type":"object"},"TextVariableSpec":{"additionalProperties":false,"description":"Text variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","query","hide","skipUrlSync"],"type":"object"},"Threshold":{"additionalProperties":false,"properties":{"color":{"type":"string"},"value":{"description":"Value null means -Infinity","type":"number"}},"required":["value","color"],"type":"object"},"ThresholdsConfig":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/ThresholdsMode"},"steps":{"items":{"$ref":"#/components/schemas/Threshold"},"type":"array"}},"required":["mode","steps"],"type":"object"},"ThresholdsMode":{"enum":["absolute","percentage"],"type":"string"},"TimeRangeOption":{"additionalProperties":false,"properties":{"display":{"default":"Last 6 hours","type":"string"},"from":{"default":"now-6h","type":"string"},"to":{"default":"now","type":"string"}},"required":["display","from","to"],"type":"object"},"TimeSettingsSpec":{"additionalProperties":false,"description":"Time configuration\nIt defines the default time config for the time picker, the refresh picker for the specific dashboard.","properties":{"autoRefresh":{"default":"","description":"Refresh rate of dashboard. Represented via interval string, e.g. \"5s\", \"1m\", \"1h\", \"1d\".\nv1: refresh","type":"string"},"autoRefreshIntervals":{"default":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"description":"Interval options available in the refresh picker dropdown.\nv1: timepicker.refresh_intervals","items":{"type":"string"},"type":"array"},"fiscalYearStartMonth":{"default":0,"description":"The month that the fiscal year starts on. 0 = January, 11 = December","type":"integer"},"from":{"default":"now-6h","description":"Start time range for dashboard.\nAccepted values are relative time strings like \"now-6h\" or absolute time strings like \"2020-07-10T08:00:00.000Z\".","type":"string"},"hideTimepicker":{"default":false,"description":"Whether timepicker is visible or not.\nv1: timepicker.hidden","type":"boolean"},"nowDelay":{"description":"Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values.\nv1: timepicker.nowDelay","type":"string"},"quickRanges":{"description":"Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard.\nv1: timepicker.quick_ranges , not exposed in the UI","items":{"$ref":"#/components/schemas/TimeRangeOption"},"type":"array"},"timezone":{"default":"browser","description":"Timezone of dashboard. Accepted values are IANA TZDB zone ID or \"browser\" or \"utc\".","type":"string"},"to":{"default":"now","description":"End time range for dashboard.\nAccepted values are relative time strings like \"now-6h\" or absolute time strings like \"2020-07-10T08:00:00.000Z\".","type":"string"},"weekStart":{"description":"Day when the week starts. Expressed by the name of the day in lowercase, e.g. \"monday\".","enum":["saturday","monday","sunday"],"type":"string"}},"required":["from","to","autoRefresh","autoRefreshIntervals","hideTimepicker","fiscalYearStartMonth"],"type":"object"},"TransformationKind":{"additionalProperties":false,"properties":{"kind":{"description":"The kind of a TransformationKind is the transformation ID","type":"string"},"spec":{"$ref":"#/components/schemas/DataTransformerConfig"}},"required":["kind","spec"],"type":"object"},"ValueMap":{"additionalProperties":false,"description":"Maps text values to a color or different display text and color.\nFor example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.","properties":{"options":{"additionalProperties":{"$ref":"#/components/schemas/ValueMappingResult"},"description":"Map with \u003cvalue_to_match\u003e: ValueMappingResult. For example: { \"10\": { text: \"Perfection!\", color: \"green\" } }","type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"ValueMapping":{"oneOf":[{"$ref":"#/components/schemas/ValueMap"},{"$ref":"#/components/schemas/RangeMap"},{"$ref":"#/components/schemas/RegexMap"},{"$ref":"#/components/schemas/SpecialValueMap"}]},"ValueMappingResult":{"additionalProperties":false,"description":"Result used as replacement with text and color when the value matches","properties":{"color":{"description":"Text to use when the value matches","type":"string"},"icon":{"description":"Icon to display when the value matches. Only specific visualizations.","type":"string"},"index":{"description":"Position in the mapping array. Only used internally.","type":"integer"},"text":{"description":"Text to display when the value matches","type":"string"}},"type":"object"},"VariableHide":{"description":"Determine if the variable shows on dashboard\nAccepted values are ` + "`" + `dontHide` + "`" + ` (show label and value), ` + "`" + `hideLabel` + "`" + ` (show value only), ` + "`" + `hideVariable` + "`" + ` (show nothing).","enum":["dontHide","hideLabel","hideVariable"],"type":"string"},"VariableKind":{"oneOf":[{"$ref":"#/components/schemas/QueryVariableKind"},{"$ref":"#/components/schemas/TextVariableKind"},{"$ref":"#/components/schemas/ConstantVariableKind"},{"$ref":"#/components/schemas/DatasourceVariableKind"},{"$ref":"#/components/schemas/IntervalVariableKind"},{"$ref":"#/components/schemas/CustomVariableKind"},{"$ref":"#/components/schemas/GroupByVariableKind"},{"$ref":"#/components/schemas/AdhocVariableKind"},{"$ref":"#/components/schemas/SwitchVariableKind"}]},"VariableOption":{"additionalProperties":false,"description":"Variable option specification","properties":{"selected":{"description":"Whether the option is selected or not","type":"boolean"},"text":{"description":"Text to be displayed for the option","oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]},"value":{"description":"Value of the option","oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]}},"required":["text","value"],"type":"object"},"VariableRefresh":{"description":"Options to config when to refresh a variable\n` + "`" + `never` + "`" + `: Never refresh the variable\n` + "`" + `onDashboardLoad` + "`" + `: Queries the data source every time the dashboard loads.\n` + "`" + `onTimeRangeChanged` + "`" + `: Queries the data source when the dashboard time range changes.","enum":["never","onDashboardLoad","onTimeRangeChanged"],"type":"string"},"VariableRegexApplyTo":{"description":"Determine whether regex applies to variable value or display text\nAccepted values are ` + "`" + `value` + "`" + ` (apply to value used in queries) or ` + "`" + `text` + "`" + ` (apply to display text shown to users)","enum":["value","text"],"type":"string"},"VariableSort":{"description":"Sort variable options\nAccepted values are:\n` + "`" + `disabled` + "`" + `: No sorting\n` + "`" + `alphabeticalAsc` + "`" + `: Alphabetical ASC\n` + "`" + `alphabeticalDesc` + "`" + `: Alphabetical DESC\n` + "`" + `numericalAsc` + "`" + `: Numerical ASC\n` + "`" + `numericalDesc` + "`" + `: Numerical DESC\n` + "`" + `alphabeticalCaseInsensitiveAsc` + "`" + `: Alphabetical Case Insensitive ASC\n` + "`" + `alphabeticalCaseInsensitiveDesc` + "`" + `: Alphabetical Case Insensitive DESC\n` + "`" + `naturalAsc` + "`" + `: Natural ASC\n` + "`" + `naturalDesc` + "`" + `: Natural DESC\nVariableSort enum with default value","enum":["disabled","alphabeticalAsc","alphabeticalDesc","numericalAsc","numericalDesc","alphabeticalCaseInsensitiveAsc","alphabeticalCaseInsensitiveDesc","naturalAsc","naturalDesc"],"type":"string"},"VizConfigKind":{"additionalProperties":false,"properties":{"kind":{"description":"The kind of a VizConfigKind is the plugin ID","type":"string"},"spec":{"$ref":"#/components/schemas/VizConfigSpec"}},"required":["kind","spec"],"type":"object"},"VizConfigSpec":{"additionalProperties":false,"description":"--- Kinds ---","properties":{"fieldConfig":{"$ref":"#/components/schemas/FieldConfigSource"},"options":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"},"pluginVersion":{"type":"string"}},"required":["pluginVersion","options","fieldConfig"],"type":"object"},"spec":{"additionalProperties":false,"properties":{"annotations":{"items":{"$ref":"#/components/schemas/AnnotationQueryKind"},"type":"array"},"cursorSync":{"$ref":"#/components/schemas/DashboardCursorSync","default":"Off","description":"Configuration of dashboard cursor sync behavior.\n\"Off\" for no shared crosshair or tooltip (default).\n\"Crosshair\" for shared crosshair.\n\"Tooltip\" for shared crosshair AND shared tooltip."},"description":{"description":"Description of dashboard.","type":"string"},"editable":{"default":true,"description":"Whether a dashboard is editable or not.","type":"boolean"},"elements":{"additionalProperties":{"$ref":"#/components/schemas/Element"},"type":"object"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"}]},"links":{"description":"Links with references to other dashboards or external websites.","items":{"$ref":"#/components/schemas/DashboardLink"},"type":"array"},"liveNow":{"description":"When set to true, the dashboard will redraw panels at an interval matching the pixel width.\nThis will keep data \"moving left\" regardless of the query refresh rate. This setting helps\navoid dashboards presenting stale live data.","type":"boolean"},"preload":{"default":false,"description":"When set to true, the dashboard will load all panels in the dashboard when it's loaded.","type":"boolean"},"revision":{"description":"Plugins only. The version of the dashboard installed together with the plugin.\nThis is used to determine if the dashboard should be updated when the plugin is updated.","type":"integer"},"tags":{"description":"Tags associated with dashboard.","items":{"type":"string"},"type":"array"},"timeSettings":{"$ref":"#/components/schemas/TimeSettingsSpec"},"title":{"description":"Title of dashboard.","type":"string"},"variables":{"description":"Configured template variables.","items":{"$ref":"#/components/schemas/VariableKind"},"type":"array"}},"required":["annotations","cursorSync","elements","layout","links","preload","tags","timeSettings","title","variables"],"type":"object"},"status":{"additionalProperties":false,"properties":{"conversion":{"$ref":"#/components/schemas/ConversionStatus","description":"Optional conversion status."}},"type":"object"}}`)
versionSchemaDashboardv2alpha1 app.VersionSchema
_ = json.Unmarshal(rawSchemaDashboardv2alpha1, &versionSchemaDashboardv2alpha1)
- rawSchemaDashboardv2beta1 = []byte(`{"Action":{"additionalProperties":false,"properties":{"confirmation":{"type":"string"},"fetch":{"$ref":"#/components/schemas/FetchOptions"},"infinity":{"$ref":"#/components/schemas/InfinityOptions"},"oneClick":{"type":"boolean"},"style":{"additionalProperties":false,"properties":{"backgroundColor":{"type":"string"}},"type":"object"},"title":{"type":"string"},"type":{"$ref":"#/components/schemas/ActionType"},"variables":{"items":{"$ref":"#/components/schemas/ActionVariable"},"type":"array"}},"required":["type","title"],"type":"object"},"ActionType":{"enum":["fetch","infinity"],"type":"string"},"ActionVariable":{"additionalProperties":false,"properties":{"key":{"type":"string"},"name":{"type":"string"},"type":{"$ref":"#/components/schemas/ActionVariableType"}},"required":["key","name","type"],"type":"object"},"ActionVariableType":{"const":"string","description":"Action variable type","type":"string"},"AdHocFilterWithLabels":{"additionalProperties":false,"description":"Define the AdHocFilterWithLabels type","properties":{"condition":{"description":"@deprecated","type":"string"},"forceEdit":{"type":"boolean"},"key":{"type":"string"},"keyLabel":{"type":"string"},"operator":{"type":"string"},"origin":{"$ref":"#/components/schemas/FilterOrigin"},"value":{"type":"string"},"valueLabels":{"items":{"type":"string"},"type":"array"},"values":{"items":{"type":"string"},"type":"array"}},"required":["key","operator","value"],"type":"object"},"AdhocVariableKind":{"additionalProperties":false,"description":"Adhoc variable kind","properties":{"datasource":{"additionalProperties":false,"properties":{"name":{"type":"string"}},"type":"object"},"group":{"type":"string"},"kind":{"const":"AdhocVariable","type":"string"},"spec":{"$ref":"#/components/schemas/AdhocVariableSpec"}},"required":["kind","group","spec"],"type":"object"},"AdhocVariableSpec":{"additionalProperties":false,"description":"Adhoc variable specification","properties":{"allowCustomValue":{"default":true,"type":"boolean"},"baseFilters":{"items":{"$ref":"#/components/schemas/AdHocFilterWithLabels"},"type":"array"},"defaultKeys":{"items":{"$ref":"#/components/schemas/MetricFindValue"},"type":"array"},"description":{"type":"string"},"filters":{"items":{"$ref":"#/components/schemas/AdHocFilterWithLabels"},"type":"array"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","baseFilters","filters","defaultKeys","hide","skipUrlSync","allowCustomValue"],"type":"object"},"AnnotationEventFieldMapping":{"additionalProperties":false,"description":"Annotation event field mapping. Defines how to map a data frame field to an annotation event field.","properties":{"regex":{"description":"Regular expression to apply to the field value","type":"string"},"source":{"default":"field","description":"Source type for the field value","type":"string"},"value":{"description":"Constant value to use when source is \"text\"","type":"string"}},"type":"object"},"AnnotationPanelFilter":{"additionalProperties":false,"properties":{"exclude":{"default":false,"description":"Should the specified panels be included or excluded","type":"boolean"},"ids":{"description":"Panel IDs that should be included or excluded","items":{"type":"integer"},"type":"array"}},"required":["ids"],"type":"object"},"AnnotationQueryKind":{"additionalProperties":false,"properties":{"kind":{"const":"AnnotationQuery","type":"string"},"spec":{"$ref":"#/components/schemas/AnnotationQuerySpec"}},"required":["kind","spec"],"type":"object"},"AnnotationQueryPlacement":{"const":"inControlsMenu","description":"Annotation Query placement. Defines where the annotation query should be displayed.\n- \"inControlsMenu\" renders the annotation query in the dashboard controls dropdown menu","type":"string"},"AnnotationQuerySpec":{"additionalProperties":false,"properties":{"builtIn":{"default":false,"type":"boolean"},"enable":{"type":"boolean"},"filter":{"$ref":"#/components/schemas/AnnotationPanelFilter"},"hide":{"type":"boolean"},"iconColor":{"type":"string"},"legacyOptions":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"Catch-all field for datasource-specific properties. Should not be available in as code tooling.","type":"object"},"mappings":{"additionalProperties":{"$ref":"#/components/schemas/AnnotationEventFieldMapping"},"description":"Mappings define how to convert data frame fields to annotation event fields.","type":"object"},"name":{"type":"string"},"placement":{"$ref":"#/components/schemas/AnnotationQueryPlacement","description":"Placement can be used to display the annotation query somewhere else on the dashboard other than the default location."},"query":{"$ref":"#/components/schemas/DataQueryKind"}},"required":["query","enable","hide","iconColor","name"],"type":"object"},"AutoGridLayoutItemKind":{"additionalProperties":false,"properties":{"kind":{"const":"AutoGridLayoutItem","type":"string"},"spec":{"$ref":"#/components/schemas/AutoGridLayoutItemSpec"}},"required":["kind","spec"],"type":"object"},"AutoGridLayoutItemSpec":{"additionalProperties":false,"properties":{"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"element":{"$ref":"#/components/schemas/ElementReference"},"repeat":{"$ref":"#/components/schemas/AutoGridRepeatOptions"}},"required":["element"],"type":"object"},"AutoGridLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"AutoGridLayout","type":"string"},"spec":{"$ref":"#/components/schemas/AutoGridLayoutSpec"}},"required":["kind","spec"],"type":"object"},"AutoGridLayoutSpec":{"additionalProperties":false,"properties":{"columnWidth":{"type":"number"},"columnWidthMode":{"default":"standard","enum":["narrow","standard","wide","custom"],"type":"string"},"fillScreen":{"default":false,"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/AutoGridLayoutItemKind"},"type":"array"},"maxColumnCount":{"default":3,"type":"number"},"rowHeight":{"type":"number"},"rowHeightMode":{"default":"standard","enum":["short","standard","tall","custom"],"type":"string"}},"required":["columnWidthMode","rowHeightMode","items"],"type":"object"},"AutoGridRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"ConditionalRenderingDataKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingData","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingDataSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingDataSpec":{"additionalProperties":false,"properties":{"value":{"type":"boolean"}},"required":["value"],"type":"object"},"ConditionalRenderingGroupKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingGroup","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingGroupSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingGroupSpec":{"additionalProperties":false,"properties":{"condition":{"enum":["and","or"],"type":"string"},"items":{"items":{"oneOf":[{"$ref":"#/components/schemas/ConditionalRenderingVariableKind"},{"$ref":"#/components/schemas/ConditionalRenderingDataKind"},{"$ref":"#/components/schemas/ConditionalRenderingTimeRangeSizeKind"}]},"type":"array"},"visibility":{"enum":["show","hide"],"type":"string"}},"required":["visibility","condition","items"],"type":"object"},"ConditionalRenderingTimeRangeSizeKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingTimeRangeSize","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingTimeRangeSizeSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingTimeRangeSizeSpec":{"additionalProperties":false,"properties":{"value":{"type":"string"}},"required":["value"],"type":"object"},"ConditionalRenderingVariableKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingVariable","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingVariableSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingVariableSpec":{"additionalProperties":false,"properties":{"operator":{"enum":["equals","notEquals","matches","notMatches"],"type":"string"},"value":{"type":"string"},"variable":{"type":"string"}},"required":["variable","operator","value"],"type":"object"},"ConstantVariableKind":{"additionalProperties":false,"description":"Constant variable kind","properties":{"kind":{"const":"ConstantVariable","type":"string"},"spec":{"$ref":"#/components/schemas/ConstantVariableSpec"}},"required":["kind","spec"],"type":"object"},"ConstantVariableSpec":{"additionalProperties":false,"description":"Constant variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","query","current","hide","skipUrlSync"],"type":"object"},"ConversionStatus":{"additionalProperties":false,"description":"ConversionStatus is the status of the conversion of the dashboard.","properties":{"error":{"description":"The error message from the conversion.\nEmpty if the conversion has not failed.","type":"string"},"failed":{"description":"Whether from another version has failed.\nIf true, means that the dashboard is not valid,\nand the caller should instead fetch the stored version.","type":"boolean"},"source":{"additionalProperties":{},"description":"The original value map[string]any","type":"object"},"storedVersion":{"description":"The version which was stored when the dashboard was created / updated.\nFetching this version should always succeed.","type":"string"}},"required":["failed"],"type":"object"},"CustomVariableKind":{"additionalProperties":false,"description":"Custom variable kind","properties":{"kind":{"const":"CustomVariable","type":"string"},"spec":{"$ref":"#/components/schemas/CustomVariableSpec"}},"required":["kind","spec"],"type":"object"},"CustomVariableSpec":{"additionalProperties":false,"description":"Custom variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"},"valuesFormat":{"enum":["csv","json"],"type":"string"}},"required":["name","query","current","options","multi","includeAll","hide","skipUrlSync","allowCustomValue"],"type":"object"},"Dashboard":{"properties":{"spec":{"$ref":"#/components/schemas/spec"},"status":{"$ref":"#/components/schemas/status"}},"required":["spec"]},"DashboardCursorSync":{"description":"\"Off\" for no shared crosshair or tooltip (default).\n\"Crosshair\" for shared crosshair.\n\"Tooltip\" for shared crosshair AND shared tooltip.","enum":["Crosshair","Tooltip","Off"],"type":"string"},"DashboardLink":{"additionalProperties":false,"description":"Links with references to other dashboards or external resources","properties":{"asDropdown":{"default":false,"description":"If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards","type":"boolean"},"icon":{"description":"Icon name to be displayed with the link","type":"string"},"includeVars":{"default":false,"description":"If true, includes current template variables values in the link as query params","type":"boolean"},"keepTime":{"default":false,"description":"If true, includes current time range in the link as query params","type":"boolean"},"placement":{"$ref":"#/components/schemas/DashboardLinkPlacement","description":"Placement can be used to display the link somewhere else on the dashboard other than above the visualisations."},"tags":{"description":"List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards","items":{"type":"string"},"type":"array"},"targetBlank":{"default":false,"description":"If true, the link will be opened in a new tab","type":"boolean"},"title":{"description":"Title to display with the link","type":"string"},"tooltip":{"description":"Tooltip to display when the user hovers their mouse over it","type":"string"},"type":{"$ref":"#/components/schemas/DashboardLinkType","description":"Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)\nFIXME: The type is generated as ` + "`" + `type: DashboardLinkType | dashboardLinkType.Link;` + "`" + ` but it should be ` + "`" + `type: DashboardLinkType` + "`" + `"},"url":{"description":"Link URL. Only required/valid if the type is link","type":"string"}},"required":["title","type","icon","tooltip","tags","asDropdown","targetBlank","includeVars","keepTime"],"type":"object"},"DashboardLinkPlacement":{"const":"inControlsMenu","description":"Dashboard Link placement. Defines where the link should be displayed.\n- \"inControlsMenu\" renders the link in bottom part of the dashboard controls dropdown menu","type":"string"},"DashboardLinkType":{"description":"Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)","enum":["link","dashboards"],"type":"string"},"DataLink":{"additionalProperties":false,"properties":{"targetBlank":{"type":"boolean"},"title":{"type":"string"},"url":{"type":"string"}},"required":["title","url"],"type":"object"},"DataQueryKind":{"additionalProperties":false,"properties":{"datasource":{"additionalProperties":false,"description":"New type for datasource reference\nNot 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.","properties":{"name":{"type":"string"}},"type":"object"},"group":{"type":"string"},"kind":{"const":"DataQuery","type":"string"},"spec":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"},"version":{"default":"v0","type":"string"}},"required":["kind","group","version","spec"],"type":"object"},"DataTopic":{"description":"A topic is attached to DataFrame metadata in query results.\nThis specifies where the data should be used.","enum":["series","annotations","alertStates"],"type":"string"},"DataTransformerConfig":{"additionalProperties":false,"description":"Transformations allow to manipulate data returned by a query before the system applies a visualization.\nUsing transformations you can: rename fields, join time series data, perform mathematical operations across queries,\nuse the output of one transformation as the input to another transformation, etc.","properties":{"disabled":{"description":"Disabled transformations are skipped","type":"boolean"},"filter":{"$ref":"#/components/schemas/MatcherConfig","description":"Optional frame matcher. When missing it will be applied to all results"},"id":{"description":"Unique identifier of transformer","type":"string"},"options":{"additionalProperties":{},"description":"Options to be passed to the transformer\nValid options depend on the transformer id","type":"object"},"topic":{"$ref":"#/components/schemas/DataTopic","description":"Where to pull DataFrames from as input to transformation"}},"required":["id","options"],"type":"object"},"DatasourceVariableKind":{"additionalProperties":false,"description":"Datasource variable kind","properties":{"kind":{"const":"DatasourceVariable","type":"string"},"spec":{"$ref":"#/components/schemas/DatasourceVariableSpec"}},"required":["kind","spec"],"type":"object"},"DatasourceVariableSpec":{"additionalProperties":false,"description":"Datasource variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"pluginId":{"default":"","type":"string"},"refresh":{"$ref":"#/components/schemas/VariableRefresh","default":"never"},"regex":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","pluginId","refresh","regex","current","options","multi","includeAll","hide","skipUrlSync","allowCustomValue"],"type":"object"},"DynamicConfigValue":{"additionalProperties":false,"properties":{"id":{"default":"","type":"string"},"value":{"additionalProperties":{},"type":"object"}},"required":["id"],"type":"object"},"Element":{"description":"Supported dashboard elements\n|* more element types in the future","oneOf":[{"$ref":"#/components/schemas/PanelKind"},{"$ref":"#/components/schemas/LibraryPanelKind"}]},"ElementReference":{"additionalProperties":false,"properties":{"kind":{"const":"ElementReference","type":"string"},"name":{"type":"string"}},"required":["kind","name"],"type":"object"},"FetchOptions":{"additionalProperties":false,"properties":{"body":{"type":"string"},"headers":{"items":{"items":{"type":"string"},"type":"array"},"type":"array"},"method":{"$ref":"#/components/schemas/HttpRequestMethod"},"queryParams":{"description":"These are 2D arrays of strings, each representing a key-value pair\nWe are defining them this way because we can't generate a go struct that\nthat would have exactly two strings in each sub-array","items":{"items":{"type":"string"},"type":"array"},"type":"array"},"url":{"type":"string"}},"required":["method","url"],"type":"object"},"FieldColor":{"additionalProperties":false,"description":"Map a field to a color.","properties":{"fixedColor":{"description":"The fixed color value for fixed or shades color modes.","type":"string"},"mode":{"$ref":"#/components/schemas/FieldColorModeId","description":"The main color scheme mode."},"seriesBy":{"$ref":"#/components/schemas/FieldColorSeriesByMode","description":"Some visualizations need to know how to assign a series color from by value color schemes."}},"required":["mode"],"type":"object"},"FieldColorModeId":{"description":"Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value.\nContinuous color interpolates a color using the percentage of a value relative to min and max.\nAccepted values are:\n` + "`" + `thresholds` + "`" + `: From thresholds. Informs Grafana to take the color from the matching threshold\n` + "`" + `palette-classic` + "`" + `: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations\n` + "`" + `palette-classic-by-name` + "`" + `: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations\n` + "`" + `continuous-viridis` + "`" + `: Continuous Viridis palette mode\n` + "`" + `continuous-magma` + "`" + `: Continuous Magma palette mode\n` + "`" + `continuous-plasma` + "`" + `: Continuous Plasma palette mode\n` + "`" + `continuous-inferno` + "`" + `: Continuous Inferno palette mode\n` + "`" + `continuous-cividis` + "`" + `: Continuous Cividis palette mode\n` + "`" + `continuous-GrYlRd` + "`" + `: Continuous Green-Yellow-Red palette mode\n` + "`" + `continuous-RdYlGr` + "`" + `: Continuous Red-Yellow-Green palette mode\n` + "`" + `continuous-BlYlRd` + "`" + `: Continuous Blue-Yellow-Red palette mode\n` + "`" + `continuous-YlRd` + "`" + `: Continuous Yellow-Red palette mode\n` + "`" + `continuous-BlPu` + "`" + `: Continuous Blue-Purple palette mode\n` + "`" + `continuous-YlBl` + "`" + `: Continuous Yellow-Blue palette mode\n` + "`" + `continuous-blues` + "`" + `: Continuous Blue palette mode\n` + "`" + `continuous-reds` + "`" + `: Continuous Red palette mode\n` + "`" + `continuous-greens` + "`" + `: Continuous Green palette mode\n` + "`" + `continuous-purples` + "`" + `: Continuous Purple palette mode\n` + "`" + `shades` + "`" + `: Shades of a single color. Specify a single color, useful in an override rule.\n` + "`" + `fixed` + "`" + `: Fixed color mode. Specify a single color, useful in an override rule.","enum":["thresholds","palette-classic","palette-classic-by-name","continuous-viridis","continuous-magma","continuous-plasma","continuous-inferno","continuous-cividis","continuous-GrYlRd","continuous-RdYlGr","continuous-BlYlRd","continuous-YlRd","continuous-BlPu","continuous-YlBl","continuous-blues","continuous-reds","continuous-greens","continuous-purples","fixed","shades"],"type":"string"},"FieldColorSeriesByMode":{"description":"Defines how to assign a series color from \"by value\" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value.","enum":["min","max","last"],"type":"string"},"FieldConfig":{"additionalProperties":false,"description":"The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.\nEach column within this structure is called a field. A field can represent a single time series or table column.\nField options allow you to change how the data is displayed in your visualizations.","properties":{"actions":{"description":"Define interactive HTTP requests that can be triggered from data visualizations.","items":{"$ref":"#/components/schemas/Action"},"type":"array"},"color":{"$ref":"#/components/schemas/FieldColor","description":"Panel color configuration"},"custom":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"custom is specified by the FieldConfig field\nin panel plugin schemas.","type":"object"},"decimals":{"description":"Specify the number of decimals Grafana includes in the rendered value.\nIf you leave this field blank, Grafana automatically truncates the number of decimals based on the value.\nFor example 1.1234 will display as 1.12 and 100.456 will display as 100.\nTo display all decimals, set the unit to ` + "`" + `String` + "`" + `.","type":"number"},"description":{"description":"Human readable field metadata","type":"string"},"displayName":{"description":"The display value for this field. This supports template variables blank is auto","type":"string"},"displayNameFromDS":{"description":"This can be used by data sources that return and explicit naming structure for values and labels\nWhen this property is configured, this value is used rather than the default naming strategy.","type":"string"},"filterable":{"description":"True if data source field supports ad-hoc filters","type":"boolean"},"links":{"description":"The behavior when clicking on a result","items":{"additionalProperties":{},"type":"object"},"type":"array"},"mappings":{"description":"Convert input values into a display string","items":{"$ref":"#/components/schemas/ValueMapping"},"type":"array"},"max":{"description":"The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.","type":"number"},"min":{"description":"The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.","type":"number"},"noValue":{"description":"Alternative to empty string","type":"string"},"path":{"description":"An explicit path to the field in the datasource. When the frame meta includes a path,\nThis will default to ` + "`" + `${frame.meta.path}/${field.name}\n\nWhen defined, this value can be used as an identifier within the datasource scope, and\nmay be used to update the results","type":"string"},"thresholds":{"$ref":"#/components/schemas/ThresholdsConfig","description":"Map numeric values to states"},"unit":{"description":"Unit a field should use. The unit you select is applied to all fields except time.\nYou can use the units ID availables in Grafana or a custom unit.\nAvailable units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts\nAs custom unit, you can use the following formats:\n` + "`" + `suffix:\u003csuffix\u003e` + "`" + ` for custom unit that should go after value.\n` + "`" + `prefix:\u003cprefix\u003e` + "`" + ` for custom unit that should go before value.\n` + "`" + `time:\u003cformat\u003e` + "`" + ` For custom date time formats type for example ` + "`" + `time:YYYY-MM-DD` + "`" + `.\n` + "`" + `si:\u003cbase scale\u003e\u003cunit characters\u003e` + "`" + ` for custom SI units. For example: ` + "`" + `si: mF` + "`" + `. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character.\n` + "`" + `count:\u003cunit\u003e` + "`" + ` for a custom count unit.\n` + "`" + `currency:\u003cunit\u003e` + "`" + ` for custom a currency unit.","type":"string"},"writeable":{"description":"True if data source can write a value to the path. Auth/authz are supported separately","type":"boolean"}},"type":"object"},"FieldConfigSource":{"additionalProperties":false,"description":"The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.\nEach column within this structure is called a field. A field can represent a single time series or table column.\nField options allow you to change how the data is displayed in your visualizations.","properties":{"defaults":{"$ref":"#/components/schemas/FieldConfig","description":"Defaults are the options applied to all fields."},"overrides":{"description":"Overrides are the options applied to specific fields overriding the defaults.","items":{"additionalProperties":false,"properties":{"__systemRef":{"description":"Describes config override rules created when interacting with Grafana.","type":"string"},"matcher":{"$ref":"#/components/schemas/MatcherConfig"},"properties":{"items":{"$ref":"#/components/schemas/DynamicConfigValue"},"type":"array"}},"required":["matcher","properties"],"type":"object"},"type":"array"}},"required":["defaults","overrides"],"type":"object"},"FilterOrigin":{"const":"dashboard","description":"Determine the origin of the adhoc variable filter","type":"string"},"GridLayoutItemKind":{"additionalProperties":false,"properties":{"kind":{"const":"GridLayoutItem","type":"string"},"spec":{"$ref":"#/components/schemas/GridLayoutItemSpec"}},"required":["kind","spec"],"type":"object"},"GridLayoutItemSpec":{"additionalProperties":false,"properties":{"element":{"$ref":"#/components/schemas/ElementReference","description":"reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference"},"height":{"type":"integer"},"repeat":{"$ref":"#/components/schemas/RepeatOptions"},"width":{"type":"integer"},"x":{"type":"integer"},"y":{"type":"integer"}},"required":["x","y","width","height","element"],"type":"object"},"GridLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"GridLayout","type":"string"},"spec":{"$ref":"#/components/schemas/GridLayoutSpec"}},"required":["kind","spec"],"type":"object"},"GridLayoutSpec":{"additionalProperties":false,"properties":{"items":{"items":{"$ref":"#/components/schemas/GridLayoutItemKind"},"type":"array"}},"required":["items"],"type":"object"},"GroupByVariableKind":{"additionalProperties":false,"description":"Group variable kind","properties":{"datasource":{"additionalProperties":false,"properties":{"name":{"type":"string"}},"type":"object"},"group":{"type":"string"},"kind":{"const":"GroupByVariable","type":"string"},"spec":{"$ref":"#/components/schemas/GroupByVariableSpec"}},"required":["kind","group","spec"],"type":"object"},"GroupByVariableSpec":{"additionalProperties":false,"description":"GroupBy variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"defaultValue":{"$ref":"#/components/schemas/VariableOption"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","options","multi","hide","skipUrlSync"],"type":"object"},"HttpRequestMethod":{"enum":["GET","PUT","POST","DELETE","PATCH"],"type":"string"},"InfinityOptions":{"additionalProperties":false,"properties":{"body":{"type":"string"},"datasourceUid":{"type":"string"},"headers":{"items":{"items":{"type":"string"},"type":"array"},"type":"array"},"method":{"$ref":"#/components/schemas/HttpRequestMethod"},"queryParams":{"description":"These are 2D arrays of strings, each representing a key-value pair\nWe are defining them this way because we can't generate a go struct that\nthat would have exactly two strings in each sub-array","items":{"items":{"type":"string"},"type":"array"},"type":"array"},"url":{"type":"string"}},"required":["method","url","datasourceUid"],"type":"object"},"IntervalVariableKind":{"additionalProperties":false,"description":"Interval variable kind","properties":{"kind":{"const":"IntervalVariable","type":"string"},"spec":{"$ref":"#/components/schemas/IntervalVariableSpec"}},"required":["kind","spec"],"type":"object"},"IntervalVariableSpec":{"additionalProperties":false,"description":"Interval variable specification","properties":{"auto":{"default":false,"type":"boolean"},"auto_count":{"default":0,"type":"integer"},"auto_min":{"default":"","type":"string"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"query":{"default":"","type":"string"},"refresh":{"const":"onTimeRangeChanged","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","query","current","options","auto","auto_min","auto_count","refresh","hide","skipUrlSync"],"type":"object"},"LibraryPanelKind":{"additionalProperties":false,"properties":{"kind":{"const":"LibraryPanel","type":"string"},"spec":{"$ref":"#/components/schemas/LibraryPanelKindSpec"}},"required":["kind","spec"],"type":"object"},"LibraryPanelKindSpec":{"additionalProperties":false,"properties":{"id":{"description":"Panel ID for the library panel in the dashboard","type":"number"},"libraryPanel":{"$ref":"#/components/schemas/LibraryPanelRef"},"title":{"description":"Title for the library panel in the dashboard","type":"string"}},"required":["id","title","libraryPanel"],"type":"object"},"LibraryPanelRef":{"additionalProperties":false,"description":"A library panel is a reusable panel that you can use in any dashboard.\nWhen you make a change to a library panel, that change propagates to all instances of where the panel is used.\nLibrary panels streamline reuse of panels across multiple dashboards.","properties":{"name":{"description":"Library panel name","type":"string"},"uid":{"description":"Library panel uid","type":"string"}},"required":["name","uid"],"type":"object"},"MappingType":{"description":"Supported value mapping types\n` + "`" + `value` + "`" + `: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.\n` + "`" + `range` + "`" + `: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.\n` + "`" + `regex` + "`" + `: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.\n` + "`" + `special` + "`" + `: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A.","enum":["value","range","regex","special"],"type":"string"},"MatcherConfig":{"additionalProperties":false,"description":"Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation.\nIt comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type.","properties":{"id":{"default":"","description":"The matcher id. This is used to find the matcher implementation from registry.","type":"string"},"options":{"additionalProperties":{},"description":"The matcher options. This is specific to the matcher implementation.","type":"object"}},"required":["id"],"type":"object"},"MetricFindValue":{"additionalProperties":false,"description":"Define the MetricFindValue type","properties":{"expandable":{"type":"boolean"},"group":{"type":"string"},"text":{"type":"string"},"value":{"oneOf":[{"type":"string"},{"type":"number"}]}},"required":["text"],"type":"object"},"PanelKind":{"additionalProperties":false,"properties":{"kind":{"const":"Panel","type":"string"},"spec":{"$ref":"#/components/schemas/PanelSpec"}},"required":["kind","spec"],"type":"object"},"PanelQueryKind":{"additionalProperties":false,"properties":{"kind":{"const":"PanelQuery","type":"string"},"spec":{"$ref":"#/components/schemas/PanelQuerySpec"}},"required":["kind","spec"],"type":"object"},"PanelQuerySpec":{"additionalProperties":false,"properties":{"hidden":{"type":"boolean"},"query":{"$ref":"#/components/schemas/DataQueryKind"},"refId":{"default":"A","type":"string"}},"required":["query","refId","hidden"],"type":"object"},"PanelSpec":{"additionalProperties":false,"properties":{"data":{"$ref":"#/components/schemas/QueryGroupKind"},"description":{"type":"string"},"id":{"type":"number"},"links":{"items":{"$ref":"#/components/schemas/DataLink"},"type":"array"},"title":{"type":"string"},"transparent":{"type":"boolean"},"vizConfig":{"$ref":"#/components/schemas/VizConfigKind"}},"required":["id","title","description","links","data","vizConfig"],"type":"object"},"QueryGroupKind":{"additionalProperties":false,"properties":{"kind":{"const":"QueryGroup","type":"string"},"spec":{"$ref":"#/components/schemas/QueryGroupSpec"}},"required":["kind","spec"],"type":"object"},"QueryGroupSpec":{"additionalProperties":false,"properties":{"queries":{"items":{"$ref":"#/components/schemas/PanelQueryKind"},"type":"array"},"queryOptions":{"$ref":"#/components/schemas/QueryOptionsSpec"},"transformations":{"items":{"$ref":"#/components/schemas/TransformationKind"},"type":"array"}},"required":["queries","transformations","queryOptions"],"type":"object"},"QueryOptionsSpec":{"additionalProperties":false,"properties":{"cacheTimeout":{"type":"string"},"hideTimeOverride":{"type":"boolean"},"interval":{"type":"string"},"maxDataPoints":{"type":"integer"},"queryCachingTTL":{"type":"integer"},"timeCompare":{"type":"string"},"timeFrom":{"type":"string"},"timeShift":{"type":"string"}},"type":"object"},"QueryVariableKind":{"additionalProperties":false,"description":"Query variable kind","properties":{"kind":{"const":"QueryVariable","type":"string"},"spec":{"$ref":"#/components/schemas/QueryVariableSpec"}},"required":["kind","spec"],"type":"object"},"QueryVariableSpec":{"additionalProperties":false,"description":"Query variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"definition":{"type":"string"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"placeholder":{"type":"string"},"query":{"$ref":"#/components/schemas/DataQueryKind"},"refresh":{"$ref":"#/components/schemas/VariableRefresh","default":"never"},"regex":{"default":"","type":"string"},"regexApplyTo":{"$ref":"#/components/schemas/VariableRegexApplyTo","default":"value"},"skipUrlSync":{"default":false,"type":"boolean"},"sort":{"$ref":"#/components/schemas/VariableSort"},"staticOptions":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"staticOptionsOrder":{"enum":["before","after","sorted"],"type":"string"}},"required":["name","current","hide","refresh","skipUrlSync","query","regex","sort","options","multi","includeAll","allowCustomValue"],"type":"object"},"RangeMap":{"additionalProperties":false,"description":"Maps numerical ranges to a display text and color.\nFor example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.","properties":{"options":{"additionalProperties":false,"description":"Range to match against and the result to apply when the value is within the range","properties":{"from":{"description":"Min value of the range. It can be null which means -Infinity","type":"number"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value is within the range"},"to":{"description":"Max value of the range. It can be null which means +Infinity","type":"number"}},"required":["from","to","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"RegexMap":{"additionalProperties":false,"description":"Maps regular expressions to replacement text and a color.\nFor example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.","properties":{"options":{"additionalProperties":false,"description":"Regular expression to match against and the result to apply when the value matches the regex","properties":{"pattern":{"description":"Regular expression to match against","type":"string"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value matches the regex"}},"required":["pattern","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"RepeatMode":{"const":"variable","description":"other repeat modes will be added in the future: label, frame","type":"string"},"RepeatOptions":{"additionalProperties":false,"properties":{"direction":{"enum":["h","v"],"type":"string"},"maxPerRow":{"type":"integer"},"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"RowRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"RowsLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"RowsLayout","type":"string"},"spec":{"$ref":"#/components/schemas/RowsLayoutSpec"}},"required":["kind","spec"],"type":"object"},"RowsLayoutRowKind":{"additionalProperties":false,"properties":{"kind":{"const":"RowsLayoutRow","type":"string"},"spec":{"$ref":"#/components/schemas/RowsLayoutRowSpec"}},"required":["kind","spec"],"type":"object"},"RowsLayoutRowSpec":{"additionalProperties":false,"properties":{"collapse":{"type":"boolean"},"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"fillScreen":{"type":"boolean"},"hideHeader":{"type":"boolean"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"}]},"repeat":{"$ref":"#/components/schemas/RowRepeatOptions"},"title":{"type":"string"}},"required":["layout"],"type":"object"},"RowsLayoutSpec":{"additionalProperties":false,"properties":{"rows":{"items":{"$ref":"#/components/schemas/RowsLayoutRowKind"},"type":"array"}},"required":["rows"],"type":"object"},"SpecialValueMap":{"additionalProperties":false,"description":"Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color.\nSee SpecialValueMatch to see the list of special values.\nFor example, you can configure a special value mapping so that null values appear as N/A.","properties":{"options":{"additionalProperties":false,"properties":{"match":{"$ref":"#/components/schemas/SpecialValueMatch","description":"Special value to match against"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value matches the special value"}},"required":["match","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"SpecialValueMatch":{"description":"Special value types supported by the ` + "`" + `SpecialValueMap` + "`" + `","enum":["true","false","null","nan","null+nan","empty"],"type":"string"},"SwitchVariableKind":{"additionalProperties":false,"properties":{"kind":{"const":"SwitchVariable","type":"string"},"spec":{"$ref":"#/components/schemas/SwitchVariableSpec"}},"required":["kind","spec"],"type":"object"},"SwitchVariableSpec":{"additionalProperties":false,"properties":{"current":{"default":"false","type":"string"},"description":{"type":"string"},"disabledValue":{"default":"false","type":"string"},"enabledValue":{"default":"true","type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","enabledValue","disabledValue","hide","skipUrlSync"],"type":"object"},"TabRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"TabsLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"TabsLayout","type":"string"},"spec":{"$ref":"#/components/schemas/TabsLayoutSpec"}},"required":["kind","spec"],"type":"object"},"TabsLayoutSpec":{"additionalProperties":false,"properties":{"tabs":{"items":{"$ref":"#/components/schemas/TabsLayoutTabKind"},"type":"array"}},"required":["tabs"],"type":"object"},"TabsLayoutTabKind":{"additionalProperties":false,"properties":{"kind":{"const":"TabsLayoutTab","type":"string"},"spec":{"$ref":"#/components/schemas/TabsLayoutTabSpec"}},"required":["kind","spec"],"type":"object"},"TabsLayoutTabSpec":{"additionalProperties":false,"properties":{"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"}]},"repeat":{"$ref":"#/components/schemas/TabRepeatOptions"},"title":{"type":"string"}},"required":["layout"],"type":"object"},"TextVariableKind":{"additionalProperties":false,"description":"Text variable kind","properties":{"kind":{"const":"TextVariable","type":"string"},"spec":{"$ref":"#/components/schemas/TextVariableSpec"}},"required":["kind","spec"],"type":"object"},"TextVariableSpec":{"additionalProperties":false,"description":"Text variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","query","hide","skipUrlSync"],"type":"object"},"Threshold":{"additionalProperties":false,"properties":{"color":{"type":"string"},"value":{"description":"Value null means -Infinity","type":"number"}},"required":["value","color"],"type":"object"},"ThresholdsConfig":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/ThresholdsMode"},"steps":{"items":{"$ref":"#/components/schemas/Threshold"},"type":"array"}},"required":["mode","steps"],"type":"object"},"ThresholdsMode":{"enum":["absolute","percentage"],"type":"string"},"TimeRangeOption":{"additionalProperties":false,"properties":{"display":{"default":"Last 6 hours","type":"string"},"from":{"default":"now-6h","type":"string"},"to":{"default":"now","type":"string"}},"required":["display","from","to"],"type":"object"},"TimeSettingsSpec":{"additionalProperties":false,"description":"Time configuration\nIt defines the default time config for the time picker, the refresh picker for the specific dashboard.","properties":{"autoRefresh":{"default":"","description":"Refresh rate of dashboard. Represented via interval string, e.g. \"5s\", \"1m\", \"1h\", \"1d\".\nv1: refresh","type":"string"},"autoRefreshIntervals":{"default":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"description":"Interval options available in the refresh picker dropdown.\nv1: timepicker.refresh_intervals","items":{"type":"string"},"type":"array"},"fiscalYearStartMonth":{"default":0,"description":"The month that the fiscal year starts on. 0 = January, 11 = December","type":"integer"},"from":{"default":"now-6h","description":"Start time range for dashboard.\nAccepted values are relative time strings like \"now-6h\" or absolute time strings like \"2020-07-10T08:00:00.000Z\".","type":"string"},"hideTimepicker":{"default":false,"description":"Whether timepicker is visible or not.\nv1: timepicker.hidden","type":"boolean"},"nowDelay":{"description":"Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values.\nv1: timepicker.nowDelay","type":"string"},"quickRanges":{"description":"Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard.\nv1: timepicker.quick_ranges , not exposed in the UI","items":{"$ref":"#/components/schemas/TimeRangeOption"},"type":"array"},"timezone":{"default":"browser","description":"Timezone of dashboard. Accepted values are IANA TZDB zone ID or \"browser\" or \"utc\".","type":"string"},"to":{"default":"now","description":"End time range for dashboard.\nAccepted values are relative time strings like \"now-6h\" or absolute time strings like \"2020-07-10T08:00:00.000Z\".","type":"string"},"weekStart":{"description":"Day when the week starts. Expressed by the name of the day in lowercase, e.g. \"monday\".","enum":["saturday","monday","sunday"],"type":"string"}},"required":["from","to","autoRefresh","autoRefreshIntervals","hideTimepicker","fiscalYearStartMonth"],"type":"object"},"TransformationKind":{"additionalProperties":false,"properties":{"kind":{"description":"The kind of a TransformationKind is the transformation ID","type":"string"},"spec":{"$ref":"#/components/schemas/DataTransformerConfig"}},"required":["kind","spec"],"type":"object"},"ValueMap":{"additionalProperties":false,"description":"Maps text values to a color or different display text and color.\nFor example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.","properties":{"options":{"additionalProperties":{"$ref":"#/components/schemas/ValueMappingResult"},"description":"Map with \u003cvalue_to_match\u003e: ValueMappingResult. For example: { \"10\": { text: \"Perfection!\", color: \"green\" } }","type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"ValueMapping":{"oneOf":[{"$ref":"#/components/schemas/ValueMap"},{"$ref":"#/components/schemas/RangeMap"},{"$ref":"#/components/schemas/RegexMap"},{"$ref":"#/components/schemas/SpecialValueMap"}]},"ValueMappingResult":{"additionalProperties":false,"description":"Result used as replacement with text and color when the value matches","properties":{"color":{"description":"Text to use when the value matches","type":"string"},"icon":{"description":"Icon to display when the value matches. Only specific visualizations.","type":"string"},"index":{"description":"Position in the mapping array. Only used internally.","type":"integer"},"text":{"description":"Text to display when the value matches","type":"string"}},"type":"object"},"VariableHide":{"description":"Determine if the variable shows on dashboard\nAccepted values are ` + "`" + `dontHide` + "`" + ` (show label and value), ` + "`" + `hideLabel` + "`" + ` (show value only), ` + "`" + `hideVariable` + "`" + ` (show nothing), ` + "`" + `inControlsMenu` + "`" + ` (show in a drop-down menu).","enum":["dontHide","hideLabel","hideVariable","inControlsMenu"],"type":"string"},"VariableKind":{"oneOf":[{"$ref":"#/components/schemas/QueryVariableKind"},{"$ref":"#/components/schemas/TextVariableKind"},{"$ref":"#/components/schemas/ConstantVariableKind"},{"$ref":"#/components/schemas/DatasourceVariableKind"},{"$ref":"#/components/schemas/IntervalVariableKind"},{"$ref":"#/components/schemas/CustomVariableKind"},{"$ref":"#/components/schemas/GroupByVariableKind"},{"$ref":"#/components/schemas/AdhocVariableKind"},{"$ref":"#/components/schemas/SwitchVariableKind"}]},"VariableOption":{"additionalProperties":false,"description":"Variable option specification","properties":{"selected":{"description":"Whether the option is selected or not","type":"boolean"},"text":{"description":"Text to be displayed for the option","oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]},"value":{"description":"Value of the option","oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]}},"required":["text","value"],"type":"object"},"VariableRefresh":{"description":"Options to config when to refresh a variable\n` + "`" + `never` + "`" + `: Never refresh the variable\n` + "`" + `onDashboardLoad` + "`" + `: Queries the data source every time the dashboard loads.\n` + "`" + `onTimeRangeChanged` + "`" + `: Queries the data source when the dashboard time range changes.","enum":["never","onDashboardLoad","onTimeRangeChanged"],"type":"string"},"VariableRegexApplyTo":{"description":"Determine whether regex applies to variable value or display text\nAccepted values are ` + "`" + `value` + "`" + ` (apply to value used in queries) or ` + "`" + `text` + "`" + ` (apply to display text shown to users)","enum":["value","text"],"type":"string"},"VariableSort":{"description":"Sort variable options\nAccepted values are:\n` + "`" + `disabled` + "`" + `: No sorting\n` + "`" + `alphabeticalAsc` + "`" + `: Alphabetical ASC\n` + "`" + `alphabeticalDesc` + "`" + `: Alphabetical DESC\n` + "`" + `numericalAsc` + "`" + `: Numerical ASC\n` + "`" + `numericalDesc` + "`" + `: Numerical DESC\n` + "`" + `alphabeticalCaseInsensitiveAsc` + "`" + `: Alphabetical Case Insensitive ASC\n` + "`" + `alphabeticalCaseInsensitiveDesc` + "`" + `: Alphabetical Case Insensitive DESC\n` + "`" + `naturalAsc` + "`" + `: Natural ASC\n` + "`" + `naturalDesc` + "`" + `: Natural DESC\nVariableSort enum with default value","enum":["disabled","alphabeticalAsc","alphabeticalDesc","numericalAsc","numericalDesc","alphabeticalCaseInsensitiveAsc","alphabeticalCaseInsensitiveDesc","naturalAsc","naturalDesc"],"type":"string"},"VizConfigKind":{"additionalProperties":false,"properties":{"group":{"description":"The group is the plugin ID","type":"string"},"kind":{"const":"VizConfig","type":"string"},"spec":{"$ref":"#/components/schemas/VizConfigSpec"},"version":{"type":"string"}},"required":["kind","group","version","spec"],"type":"object"},"VizConfigSpec":{"additionalProperties":false,"description":"--- Kinds ---","properties":{"fieldConfig":{"$ref":"#/components/schemas/FieldConfigSource"},"options":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"}},"required":["options","fieldConfig"],"type":"object"},"spec":{"additionalProperties":false,"properties":{"annotations":{"items":{"$ref":"#/components/schemas/AnnotationQueryKind"},"type":"array"},"cursorSync":{"$ref":"#/components/schemas/DashboardCursorSync","default":"Off","description":"Configuration of dashboard cursor sync behavior.\n\"Off\" for no shared crosshair or tooltip (default).\n\"Crosshair\" for shared crosshair.\n\"Tooltip\" for shared crosshair AND shared tooltip."},"description":{"description":"Description of dashboard.","type":"string"},"editable":{"default":true,"description":"Whether a dashboard is editable or not.","type":"boolean"},"elements":{"additionalProperties":{"$ref":"#/components/schemas/Element"},"type":"object"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"}]},"links":{"description":"Links with references to other dashboards or external websites.","items":{"$ref":"#/components/schemas/DashboardLink"},"type":"array"},"liveNow":{"description":"When set to true, the dashboard will redraw panels at an interval matching the pixel width.\nThis will keep data \"moving left\" regardless of the query refresh rate. This setting helps\navoid dashboards presenting stale live data.","type":"boolean"},"preload":{"default":false,"description":"When set to true, the dashboard will load all panels in the dashboard when it's loaded.","type":"boolean"},"revision":{"description":"Plugins only. The version of the dashboard installed together with the plugin.\nThis is used to determine if the dashboard should be updated when the plugin is updated.","type":"integer"},"tags":{"description":"Tags associated with dashboard.","items":{"type":"string"},"type":"array"},"timeSettings":{"$ref":"#/components/schemas/TimeSettingsSpec"},"title":{"description":"Title of dashboard.","type":"string"},"variables":{"description":"Configured template variables.","items":{"$ref":"#/components/schemas/VariableKind"},"type":"array"}},"required":["annotations","cursorSync","elements","layout","links","preload","tags","timeSettings","title","variables"],"type":"object"},"status":{"additionalProperties":false,"properties":{"conversion":{"$ref":"#/components/schemas/ConversionStatus","description":"Optional conversion status."}},"type":"object"}}`)
+ rawSchemaDashboardv2beta1 = []byte(`{"Action":{"additionalProperties":false,"properties":{"confirmation":{"type":"string"},"fetch":{"$ref":"#/components/schemas/FetchOptions"},"infinity":{"$ref":"#/components/schemas/InfinityOptions"},"oneClick":{"type":"boolean"},"style":{"additionalProperties":false,"properties":{"backgroundColor":{"type":"string"}},"type":"object"},"title":{"type":"string"},"type":{"$ref":"#/components/schemas/ActionType"},"variables":{"items":{"$ref":"#/components/schemas/ActionVariable"},"type":"array"}},"required":["type","title"],"type":"object"},"ActionType":{"enum":["fetch","infinity"],"type":"string"},"ActionVariable":{"additionalProperties":false,"properties":{"key":{"type":"string"},"name":{"type":"string"},"type":{"$ref":"#/components/schemas/ActionVariableType"}},"required":["key","name","type"],"type":"object"},"ActionVariableType":{"const":"string","description":"Action variable type","type":"string"},"AdHocFilterWithLabels":{"additionalProperties":false,"description":"Define the AdHocFilterWithLabels type","properties":{"condition":{"description":"@deprecated","type":"string"},"forceEdit":{"type":"boolean"},"key":{"type":"string"},"keyLabel":{"type":"string"},"operator":{"type":"string"},"origin":{"$ref":"#/components/schemas/FilterOrigin"},"value":{"type":"string"},"valueLabels":{"items":{"type":"string"},"type":"array"},"values":{"items":{"type":"string"},"type":"array"}},"required":["key","operator","value"],"type":"object"},"AdhocVariableKind":{"additionalProperties":false,"description":"Adhoc variable kind","properties":{"datasource":{"additionalProperties":false,"properties":{"name":{"type":"string"}},"type":"object"},"group":{"type":"string"},"kind":{"const":"AdhocVariable","type":"string"},"spec":{"$ref":"#/components/schemas/AdhocVariableSpec"}},"required":["kind","group","spec"],"type":"object"},"AdhocVariableSpec":{"additionalProperties":false,"description":"Adhoc variable specification","properties":{"allowCustomValue":{"default":true,"type":"boolean"},"baseFilters":{"items":{"$ref":"#/components/schemas/AdHocFilterWithLabels"},"type":"array"},"defaultKeys":{"items":{"$ref":"#/components/schemas/MetricFindValue"},"type":"array"},"description":{"type":"string"},"filters":{"items":{"$ref":"#/components/schemas/AdHocFilterWithLabels"},"type":"array"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","baseFilters","filters","defaultKeys","hide","skipUrlSync","allowCustomValue"],"type":"object"},"AnnotationEventFieldMapping":{"additionalProperties":false,"description":"Annotation event field mapping. Defines how to map a data frame field to an annotation event field.","properties":{"regex":{"description":"Regular expression to apply to the field value","type":"string"},"source":{"default":"field","description":"Source type for the field value","type":"string"},"value":{"description":"Constant value to use when source is \"text\"","type":"string"}},"type":"object"},"AnnotationPanelFilter":{"additionalProperties":false,"properties":{"exclude":{"default":false,"description":"Should the specified panels be included or excluded","type":"boolean"},"ids":{"description":"Panel IDs that should be included or excluded","items":{"type":"integer"},"type":"array"}},"required":["ids"],"type":"object"},"AnnotationQueryKind":{"additionalProperties":false,"properties":{"kind":{"const":"AnnotationQuery","type":"string"},"spec":{"$ref":"#/components/schemas/AnnotationQuerySpec"}},"required":["kind","spec"],"type":"object"},"AnnotationQueryPlacement":{"const":"inControlsMenu","description":"Annotation Query placement. Defines where the annotation query should be displayed.\n- \"inControlsMenu\" renders the annotation query in the dashboard controls dropdown menu","type":"string"},"AnnotationQuerySpec":{"additionalProperties":false,"properties":{"builtIn":{"default":false,"type":"boolean"},"enable":{"type":"boolean"},"filter":{"$ref":"#/components/schemas/AnnotationPanelFilter"},"hide":{"type":"boolean"},"iconColor":{"type":"string"},"legacyOptions":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"Catch-all field for datasource-specific properties. Should not be available in as code tooling.","type":"object"},"mappings":{"additionalProperties":{"$ref":"#/components/schemas/AnnotationEventFieldMapping"},"description":"Mappings define how to convert data frame fields to annotation event fields.","type":"object"},"name":{"type":"string"},"placement":{"$ref":"#/components/schemas/AnnotationQueryPlacement","description":"Placement can be used to display the annotation query somewhere else on the dashboard other than the default location."},"query":{"$ref":"#/components/schemas/DataQueryKind"}},"required":["query","enable","hide","iconColor","name"],"type":"object"},"AutoGridLayoutItemKind":{"additionalProperties":false,"properties":{"kind":{"const":"AutoGridLayoutItem","type":"string"},"spec":{"$ref":"#/components/schemas/AutoGridLayoutItemSpec"}},"required":["kind","spec"],"type":"object"},"AutoGridLayoutItemSpec":{"additionalProperties":false,"properties":{"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"element":{"$ref":"#/components/schemas/ElementReference"},"repeat":{"$ref":"#/components/schemas/AutoGridRepeatOptions"}},"required":["element"],"type":"object"},"AutoGridLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"AutoGridLayout","type":"string"},"spec":{"$ref":"#/components/schemas/AutoGridLayoutSpec"}},"required":["kind","spec"],"type":"object"},"AutoGridLayoutSpec":{"additionalProperties":false,"properties":{"columnWidth":{"type":"number"},"columnWidthMode":{"default":"standard","enum":["narrow","standard","wide","custom"],"type":"string"},"fillScreen":{"default":false,"type":"boolean"},"items":{"items":{"$ref":"#/components/schemas/AutoGridLayoutItemKind"},"type":"array"},"maxColumnCount":{"default":3,"type":"number"},"rowHeight":{"type":"number"},"rowHeightMode":{"default":"standard","enum":["short","standard","tall","custom"],"type":"string"}},"required":["columnWidthMode","rowHeightMode","items"],"type":"object"},"AutoGridRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"ConditionalRenderingDataKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingData","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingDataSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingDataSpec":{"additionalProperties":false,"properties":{"value":{"type":"boolean"}},"required":["value"],"type":"object"},"ConditionalRenderingGroupKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingGroup","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingGroupSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingGroupSpec":{"additionalProperties":false,"properties":{"condition":{"enum":["and","or"],"type":"string"},"items":{"items":{"oneOf":[{"$ref":"#/components/schemas/ConditionalRenderingVariableKind"},{"$ref":"#/components/schemas/ConditionalRenderingDataKind"},{"$ref":"#/components/schemas/ConditionalRenderingTimeRangeSizeKind"}]},"type":"array"},"visibility":{"enum":["show","hide"],"type":"string"}},"required":["visibility","condition","items"],"type":"object"},"ConditionalRenderingTimeRangeSizeKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingTimeRangeSize","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingTimeRangeSizeSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingTimeRangeSizeSpec":{"additionalProperties":false,"properties":{"value":{"type":"string"}},"required":["value"],"type":"object"},"ConditionalRenderingVariableKind":{"additionalProperties":false,"properties":{"kind":{"const":"ConditionalRenderingVariable","type":"string"},"spec":{"$ref":"#/components/schemas/ConditionalRenderingVariableSpec"}},"required":["kind","spec"],"type":"object"},"ConditionalRenderingVariableSpec":{"additionalProperties":false,"properties":{"operator":{"enum":["equals","notEquals","matches","notMatches"],"type":"string"},"value":{"type":"string"},"variable":{"type":"string"}},"required":["variable","operator","value"],"type":"object"},"ConstantVariableKind":{"additionalProperties":false,"description":"Constant variable kind","properties":{"kind":{"const":"ConstantVariable","type":"string"},"spec":{"$ref":"#/components/schemas/ConstantVariableSpec"}},"required":["kind","spec"],"type":"object"},"ConstantVariableSpec":{"additionalProperties":false,"description":"Constant variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","query","current","hide","skipUrlSync"],"type":"object"},"ConversionStatus":{"additionalProperties":false,"description":"ConversionStatus is the status of the conversion of the dashboard.","properties":{"error":{"description":"The error message from the conversion.\nEmpty if the conversion has not failed.","type":"string"},"failed":{"description":"Whether from another version has failed.\nIf true, means that the dashboard is not valid,\nand the caller should instead fetch the stored version.","type":"boolean"},"source":{"additionalProperties":{},"description":"The original value map[string]any","type":"object"},"storedVersion":{"description":"The version which was stored when the dashboard was created / updated.\nFetching this version should always succeed.","type":"string"}},"required":["failed"],"type":"object"},"CustomVariableKind":{"additionalProperties":false,"description":"Custom variable kind","properties":{"kind":{"const":"CustomVariable","type":"string"},"spec":{"$ref":"#/components/schemas/CustomVariableSpec"}},"required":["kind","spec"],"type":"object"},"CustomVariableSpec":{"additionalProperties":false,"description":"Custom variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"},"valuesFormat":{"enum":["csv","json"],"type":"string"}},"required":["name","query","current","options","multi","includeAll","hide","skipUrlSync","allowCustomValue"],"type":"object"},"Dashboard":{"properties":{"spec":{"$ref":"#/components/schemas/spec"},"status":{"$ref":"#/components/schemas/status"}},"required":["spec"]},"DashboardCursorSync":{"description":"\"Off\" for no shared crosshair or tooltip (default).\n\"Crosshair\" for shared crosshair.\n\"Tooltip\" for shared crosshair AND shared tooltip.","enum":["Crosshair","Tooltip","Off"],"type":"string"},"DashboardLink":{"additionalProperties":false,"description":"Links with references to other dashboards or external resources","properties":{"asDropdown":{"default":false,"description":"If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards","type":"boolean"},"icon":{"description":"Icon name to be displayed with the link","type":"string"},"includeVars":{"default":false,"description":"If true, includes current template variables values in the link as query params","type":"boolean"},"keepTime":{"default":false,"description":"If true, includes current time range in the link as query params","type":"boolean"},"placement":{"$ref":"#/components/schemas/DashboardLinkPlacement","description":"Placement can be used to display the link somewhere else on the dashboard other than above the visualisations."},"tags":{"description":"List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards","items":{"type":"string"},"type":"array"},"targetBlank":{"default":false,"description":"If true, the link will be opened in a new tab","type":"boolean"},"title":{"description":"Title to display with the link","type":"string"},"tooltip":{"description":"Tooltip to display when the user hovers their mouse over it","type":"string"},"type":{"$ref":"#/components/schemas/DashboardLinkType","description":"Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)\nFIXME: The type is generated as ` + "`" + `type: DashboardLinkType | dashboardLinkType.Link;` + "`" + ` but it should be ` + "`" + `type: DashboardLinkType` + "`" + `"},"url":{"description":"Link URL. Only required/valid if the type is link","type":"string"}},"required":["title","type","icon","tooltip","tags","asDropdown","targetBlank","includeVars","keepTime"],"type":"object"},"DashboardLinkPlacement":{"const":"inControlsMenu","description":"Dashboard Link placement. Defines where the link should be displayed.\n- \"inControlsMenu\" renders the link in bottom part of the dashboard controls dropdown menu","type":"string"},"DashboardLinkType":{"description":"Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)","enum":["link","dashboards"],"type":"string"},"DataLink":{"additionalProperties":false,"properties":{"targetBlank":{"type":"boolean"},"title":{"type":"string"},"url":{"type":"string"}},"required":["title","url"],"type":"object"},"DataQueryKind":{"additionalProperties":false,"properties":{"datasource":{"additionalProperties":false,"description":"New type for datasource reference\nNot 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.","properties":{"name":{"type":"string"}},"type":"object"},"group":{"type":"string"},"kind":{"const":"DataQuery","type":"string"},"spec":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"},"version":{"default":"v0","type":"string"}},"required":["kind","group","version","spec"],"type":"object"},"DataTopic":{"description":"A topic is attached to DataFrame metadata in query results.\nThis specifies where the data should be used.","enum":["series","annotations","alertStates"],"type":"string"},"DataTransformerConfig":{"additionalProperties":false,"description":"Transformations allow to manipulate data returned by a query before the system applies a visualization.\nUsing transformations you can: rename fields, join time series data, perform mathematical operations across queries,\nuse the output of one transformation as the input to another transformation, etc.","properties":{"disabled":{"description":"Disabled transformations are skipped","type":"boolean"},"filter":{"$ref":"#/components/schemas/MatcherConfig","description":"Optional frame matcher. When missing it will be applied to all results"},"id":{"description":"Unique identifier of transformer","type":"string"},"options":{"additionalProperties":{},"description":"Options to be passed to the transformer\nValid options depend on the transformer id","type":"object"},"topic":{"$ref":"#/components/schemas/DataTopic","description":"Where to pull DataFrames from as input to transformation"}},"required":["id","options"],"type":"object"},"DatasourceVariableKind":{"additionalProperties":false,"description":"Datasource variable kind","properties":{"kind":{"const":"DatasourceVariable","type":"string"},"spec":{"$ref":"#/components/schemas/DatasourceVariableSpec"}},"required":["kind","spec"],"type":"object"},"DatasourceVariableSpec":{"additionalProperties":false,"description":"Datasource variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"pluginId":{"default":"","type":"string"},"refresh":{"$ref":"#/components/schemas/VariableRefresh","default":"never"},"regex":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","pluginId","refresh","regex","current","options","multi","includeAll","hide","skipUrlSync","allowCustomValue"],"type":"object"},"DynamicConfigValue":{"additionalProperties":false,"properties":{"id":{"default":"","type":"string"},"value":{"additionalProperties":{},"type":"object"}},"required":["id"],"type":"object"},"Element":{"description":"Supported dashboard elements\n|* more element types in the future","oneOf":[{"$ref":"#/components/schemas/PanelKind"},{"$ref":"#/components/schemas/LibraryPanelKind"}]},"ElementReference":{"additionalProperties":false,"properties":{"kind":{"const":"ElementReference","type":"string"},"name":{"type":"string"}},"required":["kind","name"],"type":"object"},"FetchOptions":{"additionalProperties":false,"properties":{"body":{"type":"string"},"headers":{"items":{"items":{"type":"string"},"type":"array"},"type":"array"},"method":{"$ref":"#/components/schemas/HttpRequestMethod"},"queryParams":{"description":"These are 2D arrays of strings, each representing a key-value pair\nWe are defining them this way because we can't generate a go struct that\nthat would have exactly two strings in each sub-array","items":{"items":{"type":"string"},"type":"array"},"type":"array"},"url":{"type":"string"}},"required":["method","url"],"type":"object"},"FieldColor":{"additionalProperties":false,"description":"Map a field to a color.","properties":{"fixedColor":{"description":"The fixed color value for fixed or shades color modes.","type":"string"},"mode":{"$ref":"#/components/schemas/FieldColorModeId","description":"The main color scheme mode."},"seriesBy":{"$ref":"#/components/schemas/FieldColorSeriesByMode","description":"Some visualizations need to know how to assign a series color from by value color schemes."}},"required":["mode"],"type":"object"},"FieldColorModeId":{"description":"Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value.\nContinuous color interpolates a color using the percentage of a value relative to min and max.\nAccepted values are:\n` + "`" + `thresholds` + "`" + `: From thresholds. Informs Grafana to take the color from the matching threshold\n` + "`" + `palette-classic` + "`" + `: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations\n` + "`" + `palette-classic-by-name` + "`" + `: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations\n` + "`" + `continuous-viridis` + "`" + `: Continuous Viridis palette mode\n` + "`" + `continuous-magma` + "`" + `: Continuous Magma palette mode\n` + "`" + `continuous-plasma` + "`" + `: Continuous Plasma palette mode\n` + "`" + `continuous-inferno` + "`" + `: Continuous Inferno palette mode\n` + "`" + `continuous-cividis` + "`" + `: Continuous Cividis palette mode\n` + "`" + `continuous-GrYlRd` + "`" + `: Continuous Green-Yellow-Red palette mode\n` + "`" + `continuous-RdYlGr` + "`" + `: Continuous Red-Yellow-Green palette mode\n` + "`" + `continuous-BlYlRd` + "`" + `: Continuous Blue-Yellow-Red palette mode\n` + "`" + `continuous-YlRd` + "`" + `: Continuous Yellow-Red palette mode\n` + "`" + `continuous-BlPu` + "`" + `: Continuous Blue-Purple palette mode\n` + "`" + `continuous-YlBl` + "`" + `: Continuous Yellow-Blue palette mode\n` + "`" + `continuous-blues` + "`" + `: Continuous Blue palette mode\n` + "`" + `continuous-reds` + "`" + `: Continuous Red palette mode\n` + "`" + `continuous-greens` + "`" + `: Continuous Green palette mode\n` + "`" + `continuous-purples` + "`" + `: Continuous Purple palette mode\n` + "`" + `shades` + "`" + `: Shades of a single color. Specify a single color, useful in an override rule.\n` + "`" + `fixed` + "`" + `: Fixed color mode. Specify a single color, useful in an override rule.","enum":["thresholds","palette-classic","palette-classic-by-name","continuous-viridis","continuous-magma","continuous-plasma","continuous-inferno","continuous-cividis","continuous-GrYlRd","continuous-RdYlGr","continuous-BlYlRd","continuous-YlRd","continuous-BlPu","continuous-YlBl","continuous-blues","continuous-reds","continuous-greens","continuous-purples","fixed","shades"],"type":"string"},"FieldColorSeriesByMode":{"description":"Defines how to assign a series color from \"by value\" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value.","enum":["min","max","last"],"type":"string"},"FieldConfig":{"additionalProperties":false,"description":"The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.\nEach column within this structure is called a field. A field can represent a single time series or table column.\nField options allow you to change how the data is displayed in your visualizations.","properties":{"actions":{"description":"Define interactive HTTP requests that can be triggered from data visualizations.","items":{"$ref":"#/components/schemas/Action"},"type":"array"},"color":{"$ref":"#/components/schemas/FieldColor","description":"Panel color configuration"},"custom":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"custom is specified by the FieldConfig field\nin panel plugin schemas.","type":"object"},"decimals":{"description":"Specify the number of decimals Grafana includes in the rendered value.\nIf you leave this field blank, Grafana automatically truncates the number of decimals based on the value.\nFor example 1.1234 will display as 1.12 and 100.456 will display as 100.\nTo display all decimals, set the unit to ` + "`" + `String` + "`" + `.","type":"number"},"description":{"description":"Human readable field metadata","type":"string"},"displayName":{"description":"The display value for this field. This supports template variables blank is auto","type":"string"},"displayNameFromDS":{"description":"This can be used by data sources that return and explicit naming structure for values and labels\nWhen this property is configured, this value is used rather than the default naming strategy.","type":"string"},"fieldMinMax":{"description":"Calculate min max per field","type":"boolean"},"filterable":{"description":"True if data source field supports ad-hoc filters","type":"boolean"},"links":{"description":"The behavior when clicking on a result","items":{"additionalProperties":{},"type":"object"},"type":"array"},"mappings":{"description":"Convert input values into a display string","items":{"$ref":"#/components/schemas/ValueMapping"},"type":"array"},"max":{"description":"The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.","type":"number"},"min":{"description":"The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.","type":"number"},"noValue":{"description":"Alternative to empty string","type":"string"},"nullValueMode":{"$ref":"#/components/schemas/NullValueMode","description":"How null values should be handled when calculating field stats\n\"null\" - Include null values, \"connected\" - Ignore nulls, \"null as zero\" - Treat nulls as zero"},"path":{"description":"An explicit path to the field in the datasource. When the frame meta includes a path,\nThis will default to ` + "`" + `${frame.meta.path}/${field.name}\n\nWhen defined, this value can be used as an identifier within the datasource scope, and\nmay be used to update the results","type":"string"},"thresholds":{"$ref":"#/components/schemas/ThresholdsConfig","description":"Map numeric values to states"},"unit":{"description":"Unit a field should use. The unit you select is applied to all fields except time.\nYou can use the units ID availables in Grafana or a custom unit.\nAvailable units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts\nAs custom unit, you can use the following formats:\n` + "`" + `suffix:\u003csuffix\u003e` + "`" + ` for custom unit that should go after value.\n` + "`" + `prefix:\u003cprefix\u003e` + "`" + ` for custom unit that should go before value.\n` + "`" + `time:\u003cformat\u003e` + "`" + ` For custom date time formats type for example ` + "`" + `time:YYYY-MM-DD` + "`" + `.\n` + "`" + `si:\u003cbase scale\u003e\u003cunit characters\u003e` + "`" + ` for custom SI units. For example: ` + "`" + `si: mF` + "`" + `. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character.\n` + "`" + `count:\u003cunit\u003e` + "`" + ` for a custom count unit.\n` + "`" + `currency:\u003cunit\u003e` + "`" + ` for custom a currency unit.","type":"string"},"writeable":{"description":"True if data source can write a value to the path. Auth/authz are supported separately","type":"boolean"}},"type":"object"},"FieldConfigSource":{"additionalProperties":false,"description":"The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.\nEach column within this structure is called a field. A field can represent a single time series or table column.\nField options allow you to change how the data is displayed in your visualizations.","properties":{"defaults":{"$ref":"#/components/schemas/FieldConfig","description":"Defaults are the options applied to all fields."},"overrides":{"description":"Overrides are the options applied to specific fields overriding the defaults.","items":{"additionalProperties":false,"properties":{"__systemRef":{"description":"Describes config override rules created when interacting with Grafana.","type":"string"},"matcher":{"$ref":"#/components/schemas/MatcherConfig"},"properties":{"items":{"$ref":"#/components/schemas/DynamicConfigValue"},"type":"array"}},"required":["matcher","properties"],"type":"object"},"type":"array"}},"required":["defaults","overrides"],"type":"object"},"FilterOrigin":{"const":"dashboard","description":"Determine the origin of the adhoc variable filter","type":"string"},"GridLayoutItemKind":{"additionalProperties":false,"properties":{"kind":{"const":"GridLayoutItem","type":"string"},"spec":{"$ref":"#/components/schemas/GridLayoutItemSpec"}},"required":["kind","spec"],"type":"object"},"GridLayoutItemSpec":{"additionalProperties":false,"properties":{"element":{"$ref":"#/components/schemas/ElementReference","description":"reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference"},"height":{"type":"integer"},"repeat":{"$ref":"#/components/schemas/RepeatOptions"},"width":{"type":"integer"},"x":{"type":"integer"},"y":{"type":"integer"}},"required":["x","y","width","height","element"],"type":"object"},"GridLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"GridLayout","type":"string"},"spec":{"$ref":"#/components/schemas/GridLayoutSpec"}},"required":["kind","spec"],"type":"object"},"GridLayoutSpec":{"additionalProperties":false,"properties":{"items":{"items":{"$ref":"#/components/schemas/GridLayoutItemKind"},"type":"array"}},"required":["items"],"type":"object"},"GroupByVariableKind":{"additionalProperties":false,"description":"Group variable kind","properties":{"datasource":{"additionalProperties":false,"properties":{"name":{"type":"string"}},"type":"object"},"group":{"type":"string"},"kind":{"const":"GroupByVariable","type":"string"},"spec":{"$ref":"#/components/schemas/GroupByVariableSpec"}},"required":["kind","group","spec"],"type":"object"},"GroupByVariableSpec":{"additionalProperties":false,"description":"GroupBy variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"defaultValue":{"$ref":"#/components/schemas/VariableOption"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","options","multi","hide","skipUrlSync"],"type":"object"},"HttpRequestMethod":{"enum":["GET","PUT","POST","DELETE","PATCH"],"type":"string"},"InfinityOptions":{"additionalProperties":false,"properties":{"body":{"type":"string"},"datasourceUid":{"type":"string"},"headers":{"items":{"items":{"type":"string"},"type":"array"},"type":"array"},"method":{"$ref":"#/components/schemas/HttpRequestMethod"},"queryParams":{"description":"These are 2D arrays of strings, each representing a key-value pair\nWe are defining them this way because we can't generate a go struct that\nthat would have exactly two strings in each sub-array","items":{"items":{"type":"string"},"type":"array"},"type":"array"},"url":{"type":"string"}},"required":["method","url","datasourceUid"],"type":"object"},"IntervalVariableKind":{"additionalProperties":false,"description":"Interval variable kind","properties":{"kind":{"const":"IntervalVariable","type":"string"},"spec":{"$ref":"#/components/schemas/IntervalVariableSpec"}},"required":["kind","spec"],"type":"object"},"IntervalVariableSpec":{"additionalProperties":false,"description":"Interval variable specification","properties":{"auto":{"default":false,"type":"boolean"},"auto_count":{"default":0,"type":"integer"},"auto_min":{"default":"","type":"string"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"query":{"default":"","type":"string"},"refresh":{"const":"onTimeRangeChanged","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","query","current","options","auto","auto_min","auto_count","refresh","hide","skipUrlSync"],"type":"object"},"LibraryPanelKind":{"additionalProperties":false,"properties":{"kind":{"const":"LibraryPanel","type":"string"},"spec":{"$ref":"#/components/schemas/LibraryPanelKindSpec"}},"required":["kind","spec"],"type":"object"},"LibraryPanelKindSpec":{"additionalProperties":false,"properties":{"id":{"description":"Panel ID for the library panel in the dashboard","type":"number"},"libraryPanel":{"$ref":"#/components/schemas/LibraryPanelRef"},"title":{"description":"Title for the library panel in the dashboard","type":"string"}},"required":["id","title","libraryPanel"],"type":"object"},"LibraryPanelRef":{"additionalProperties":false,"description":"A library panel is a reusable panel that you can use in any dashboard.\nWhen you make a change to a library panel, that change propagates to all instances of where the panel is used.\nLibrary panels streamline reuse of panels across multiple dashboards.","properties":{"name":{"description":"Library panel name","type":"string"},"uid":{"description":"Library panel uid","type":"string"}},"required":["name","uid"],"type":"object"},"MappingType":{"description":"Supported value mapping types\n` + "`" + `value` + "`" + `: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.\n` + "`" + `range` + "`" + `: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.\n` + "`" + `regex` + "`" + `: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.\n` + "`" + `special` + "`" + `: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A.","enum":["value","range","regex","special"],"type":"string"},"MatcherConfig":{"additionalProperties":false,"description":"Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation.\nIt comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type.","properties":{"id":{"default":"","description":"The matcher id. This is used to find the matcher implementation from registry.","type":"string"},"options":{"additionalProperties":{},"description":"The matcher options. This is specific to the matcher implementation.","type":"object"}},"required":["id"],"type":"object"},"MetricFindValue":{"additionalProperties":false,"description":"Define the MetricFindValue type","properties":{"expandable":{"type":"boolean"},"group":{"type":"string"},"text":{"type":"string"},"value":{"oneOf":[{"type":"string"},{"type":"number"}]}},"required":["text"],"type":"object"},"NullValueMode":{"description":"How null values should be handled","enum":["null","connected","null as zero"],"type":"string"},"PanelKind":{"additionalProperties":false,"properties":{"kind":{"const":"Panel","type":"string"},"spec":{"$ref":"#/components/schemas/PanelSpec"}},"required":["kind","spec"],"type":"object"},"PanelQueryKind":{"additionalProperties":false,"properties":{"kind":{"const":"PanelQuery","type":"string"},"spec":{"$ref":"#/components/schemas/PanelQuerySpec"}},"required":["kind","spec"],"type":"object"},"PanelQuerySpec":{"additionalProperties":false,"properties":{"hidden":{"type":"boolean"},"query":{"$ref":"#/components/schemas/DataQueryKind"},"refId":{"default":"A","type":"string"}},"required":["query","refId","hidden"],"type":"object"},"PanelSpec":{"additionalProperties":false,"properties":{"data":{"$ref":"#/components/schemas/QueryGroupKind"},"description":{"type":"string"},"id":{"type":"number"},"links":{"items":{"$ref":"#/components/schemas/DataLink"},"type":"array"},"title":{"type":"string"},"transparent":{"type":"boolean"},"vizConfig":{"$ref":"#/components/schemas/VizConfigKind"}},"required":["id","title","description","links","data","vizConfig"],"type":"object"},"QueryGroupKind":{"additionalProperties":false,"properties":{"kind":{"const":"QueryGroup","type":"string"},"spec":{"$ref":"#/components/schemas/QueryGroupSpec"}},"required":["kind","spec"],"type":"object"},"QueryGroupSpec":{"additionalProperties":false,"properties":{"queries":{"items":{"$ref":"#/components/schemas/PanelQueryKind"},"type":"array"},"queryOptions":{"$ref":"#/components/schemas/QueryOptionsSpec"},"transformations":{"items":{"$ref":"#/components/schemas/TransformationKind"},"type":"array"}},"required":["queries","transformations","queryOptions"],"type":"object"},"QueryOptionsSpec":{"additionalProperties":false,"properties":{"cacheTimeout":{"type":"string"},"hideTimeOverride":{"type":"boolean"},"interval":{"type":"string"},"maxDataPoints":{"type":"integer"},"queryCachingTTL":{"type":"integer"},"timeCompare":{"type":"string"},"timeFrom":{"type":"string"},"timeShift":{"type":"string"}},"type":"object"},"QueryVariableKind":{"additionalProperties":false,"description":"Query variable kind","properties":{"kind":{"const":"QueryVariable","type":"string"},"spec":{"$ref":"#/components/schemas/QueryVariableSpec"}},"required":["kind","spec"],"type":"object"},"QueryVariableSpec":{"additionalProperties":false,"description":"Query variable specification","properties":{"allValue":{"type":"string"},"allowCustomValue":{"default":true,"type":"boolean"},"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"definition":{"type":"string"},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"includeAll":{"default":false,"type":"boolean"},"label":{"type":"string"},"multi":{"default":false,"type":"boolean"},"name":{"default":"","type":"string"},"options":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"placeholder":{"type":"string"},"query":{"$ref":"#/components/schemas/DataQueryKind"},"refresh":{"$ref":"#/components/schemas/VariableRefresh","default":"never"},"regex":{"default":"","type":"string"},"regexApplyTo":{"$ref":"#/components/schemas/VariableRegexApplyTo","default":"value"},"skipUrlSync":{"default":false,"type":"boolean"},"sort":{"$ref":"#/components/schemas/VariableSort"},"staticOptions":{"items":{"$ref":"#/components/schemas/VariableOption"},"type":"array"},"staticOptionsOrder":{"enum":["before","after","sorted"],"type":"string"}},"required":["name","current","hide","refresh","skipUrlSync","query","regex","sort","options","multi","includeAll","allowCustomValue"],"type":"object"},"RangeMap":{"additionalProperties":false,"description":"Maps numerical ranges to a display text and color.\nFor example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.","properties":{"options":{"additionalProperties":false,"description":"Range to match against and the result to apply when the value is within the range","properties":{"from":{"description":"Min value of the range. It can be null which means -Infinity","type":"number"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value is within the range"},"to":{"description":"Max value of the range. It can be null which means +Infinity","type":"number"}},"required":["from","to","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"RegexMap":{"additionalProperties":false,"description":"Maps regular expressions to replacement text and a color.\nFor example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.","properties":{"options":{"additionalProperties":false,"description":"Regular expression to match against and the result to apply when the value matches the regex","properties":{"pattern":{"description":"Regular expression to match against","type":"string"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value matches the regex"}},"required":["pattern","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"RepeatMode":{"const":"variable","description":"other repeat modes will be added in the future: label, frame","type":"string"},"RepeatOptions":{"additionalProperties":false,"properties":{"direction":{"enum":["h","v"],"type":"string"},"maxPerRow":{"type":"integer"},"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"RowRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"RowsLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"RowsLayout","type":"string"},"spec":{"$ref":"#/components/schemas/RowsLayoutSpec"}},"required":["kind","spec"],"type":"object"},"RowsLayoutRowKind":{"additionalProperties":false,"properties":{"kind":{"const":"RowsLayoutRow","type":"string"},"spec":{"$ref":"#/components/schemas/RowsLayoutRowSpec"}},"required":["kind","spec"],"type":"object"},"RowsLayoutRowSpec":{"additionalProperties":false,"properties":{"collapse":{"type":"boolean"},"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"fillScreen":{"type":"boolean"},"hideHeader":{"type":"boolean"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"}]},"repeat":{"$ref":"#/components/schemas/RowRepeatOptions"},"title":{"type":"string"}},"required":["layout"],"type":"object"},"RowsLayoutSpec":{"additionalProperties":false,"properties":{"rows":{"items":{"$ref":"#/components/schemas/RowsLayoutRowKind"},"type":"array"}},"required":["rows"],"type":"object"},"SpecialValueMap":{"additionalProperties":false,"description":"Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color.\nSee SpecialValueMatch to see the list of special values.\nFor example, you can configure a special value mapping so that null values appear as N/A.","properties":{"options":{"additionalProperties":false,"properties":{"match":{"$ref":"#/components/schemas/SpecialValueMatch","description":"Special value to match against"},"result":{"$ref":"#/components/schemas/ValueMappingResult","description":"Config to apply when the value matches the special value"}},"required":["match","result"],"type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"SpecialValueMatch":{"description":"Special value types supported by the ` + "`" + `SpecialValueMap` + "`" + `","enum":["true","false","null","nan","null+nan","empty"],"type":"string"},"SwitchVariableKind":{"additionalProperties":false,"properties":{"kind":{"const":"SwitchVariable","type":"string"},"spec":{"$ref":"#/components/schemas/SwitchVariableSpec"}},"required":["kind","spec"],"type":"object"},"SwitchVariableSpec":{"additionalProperties":false,"properties":{"current":{"default":"false","type":"string"},"description":{"type":"string"},"disabledValue":{"default":"false","type":"string"},"enabledValue":{"default":"true","type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","enabledValue","disabledValue","hide","skipUrlSync"],"type":"object"},"TabRepeatOptions":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/RepeatMode"},"value":{"type":"string"}},"required":["mode","value"],"type":"object"},"TabsLayoutKind":{"additionalProperties":false,"properties":{"kind":{"const":"TabsLayout","type":"string"},"spec":{"$ref":"#/components/schemas/TabsLayoutSpec"}},"required":["kind","spec"],"type":"object"},"TabsLayoutSpec":{"additionalProperties":false,"properties":{"tabs":{"items":{"$ref":"#/components/schemas/TabsLayoutTabKind"},"type":"array"}},"required":["tabs"],"type":"object"},"TabsLayoutTabKind":{"additionalProperties":false,"properties":{"kind":{"const":"TabsLayoutTab","type":"string"},"spec":{"$ref":"#/components/schemas/TabsLayoutTabSpec"}},"required":["kind","spec"],"type":"object"},"TabsLayoutTabSpec":{"additionalProperties":false,"properties":{"conditionalRendering":{"$ref":"#/components/schemas/ConditionalRenderingGroupKind"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"}]},"repeat":{"$ref":"#/components/schemas/TabRepeatOptions"},"title":{"type":"string"}},"required":["layout"],"type":"object"},"TextVariableKind":{"additionalProperties":false,"description":"Text variable kind","properties":{"kind":{"const":"TextVariable","type":"string"},"spec":{"$ref":"#/components/schemas/TextVariableSpec"}},"required":["kind","spec"],"type":"object"},"TextVariableSpec":{"additionalProperties":false,"description":"Text variable specification","properties":{"current":{"$ref":"#/components/schemas/VariableOption","default":{"text":"","value":""}},"description":{"type":"string"},"hide":{"$ref":"#/components/schemas/VariableHide","default":"dontHide"},"label":{"type":"string"},"name":{"default":"","type":"string"},"query":{"default":"","type":"string"},"skipUrlSync":{"default":false,"type":"boolean"}},"required":["name","current","query","hide","skipUrlSync"],"type":"object"},"Threshold":{"additionalProperties":false,"properties":{"color":{"type":"string"},"value":{"description":"Value null means -Infinity","type":"number"}},"required":["value","color"],"type":"object"},"ThresholdsConfig":{"additionalProperties":false,"properties":{"mode":{"$ref":"#/components/schemas/ThresholdsMode"},"steps":{"items":{"$ref":"#/components/schemas/Threshold"},"type":"array"}},"required":["mode","steps"],"type":"object"},"ThresholdsMode":{"enum":["absolute","percentage"],"type":"string"},"TimeRangeOption":{"additionalProperties":false,"properties":{"display":{"default":"Last 6 hours","type":"string"},"from":{"default":"now-6h","type":"string"},"to":{"default":"now","type":"string"}},"required":["display","from","to"],"type":"object"},"TimeSettingsSpec":{"additionalProperties":false,"description":"Time configuration\nIt defines the default time config for the time picker, the refresh picker for the specific dashboard.","properties":{"autoRefresh":{"default":"","description":"Refresh rate of dashboard. Represented via interval string, e.g. \"5s\", \"1m\", \"1h\", \"1d\".\nv1: refresh","type":"string"},"autoRefreshIntervals":{"default":["5s","10s","30s","1m","5m","15m","30m","1h","2h","1d"],"description":"Interval options available in the refresh picker dropdown.\nv1: timepicker.refresh_intervals","items":{"type":"string"},"type":"array"},"fiscalYearStartMonth":{"default":0,"description":"The month that the fiscal year starts on. 0 = January, 11 = December","type":"integer"},"from":{"default":"now-6h","description":"Start time range for dashboard.\nAccepted values are relative time strings like \"now-6h\" or absolute time strings like \"2020-07-10T08:00:00.000Z\".","type":"string"},"hideTimepicker":{"default":false,"description":"Whether timepicker is visible or not.\nv1: timepicker.hidden","type":"boolean"},"nowDelay":{"description":"Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values.\nv1: timepicker.nowDelay","type":"string"},"quickRanges":{"description":"Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard.\nv1: timepicker.quick_ranges , not exposed in the UI","items":{"$ref":"#/components/schemas/TimeRangeOption"},"type":"array"},"timezone":{"default":"browser","description":"Timezone of dashboard. Accepted values are IANA TZDB zone ID or \"browser\" or \"utc\".","type":"string"},"to":{"default":"now","description":"End time range for dashboard.\nAccepted values are relative time strings like \"now-6h\" or absolute time strings like \"2020-07-10T08:00:00.000Z\".","type":"string"},"weekStart":{"description":"Day when the week starts. Expressed by the name of the day in lowercase, e.g. \"monday\".","enum":["saturday","monday","sunday"],"type":"string"}},"required":["from","to","autoRefresh","autoRefreshIntervals","hideTimepicker","fiscalYearStartMonth"],"type":"object"},"TransformationKind":{"additionalProperties":false,"properties":{"kind":{"description":"The kind of a TransformationKind is the transformation ID","type":"string"},"spec":{"$ref":"#/components/schemas/DataTransformerConfig"}},"required":["kind","spec"],"type":"object"},"ValueMap":{"additionalProperties":false,"description":"Maps text values to a color or different display text and color.\nFor example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.","properties":{"options":{"additionalProperties":{"$ref":"#/components/schemas/ValueMappingResult"},"description":"Map with \u003cvalue_to_match\u003e: ValueMappingResult. For example: { \"10\": { text: \"Perfection!\", color: \"green\" } }","type":"object"},"type":{"$ref":"#/components/schemas/MappingType"}},"required":["type","options"],"type":"object"},"ValueMapping":{"oneOf":[{"$ref":"#/components/schemas/ValueMap"},{"$ref":"#/components/schemas/RangeMap"},{"$ref":"#/components/schemas/RegexMap"},{"$ref":"#/components/schemas/SpecialValueMap"}]},"ValueMappingResult":{"additionalProperties":false,"description":"Result used as replacement with text and color when the value matches","properties":{"color":{"description":"Text to use when the value matches","type":"string"},"icon":{"description":"Icon to display when the value matches. Only specific visualizations.","type":"string"},"index":{"description":"Position in the mapping array. Only used internally.","type":"integer"},"text":{"description":"Text to display when the value matches","type":"string"}},"type":"object"},"VariableHide":{"description":"Determine if the variable shows on dashboard\nAccepted values are ` + "`" + `dontHide` + "`" + ` (show label and value), ` + "`" + `hideLabel` + "`" + ` (show value only), ` + "`" + `hideVariable` + "`" + ` (show nothing), ` + "`" + `inControlsMenu` + "`" + ` (show in a drop-down menu).","enum":["dontHide","hideLabel","hideVariable","inControlsMenu"],"type":"string"},"VariableKind":{"oneOf":[{"$ref":"#/components/schemas/QueryVariableKind"},{"$ref":"#/components/schemas/TextVariableKind"},{"$ref":"#/components/schemas/ConstantVariableKind"},{"$ref":"#/components/schemas/DatasourceVariableKind"},{"$ref":"#/components/schemas/IntervalVariableKind"},{"$ref":"#/components/schemas/CustomVariableKind"},{"$ref":"#/components/schemas/GroupByVariableKind"},{"$ref":"#/components/schemas/AdhocVariableKind"},{"$ref":"#/components/schemas/SwitchVariableKind"}]},"VariableOption":{"additionalProperties":false,"description":"Variable option specification","properties":{"selected":{"description":"Whether the option is selected or not","type":"boolean"},"text":{"description":"Text to be displayed for the option","oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]},"value":{"description":"Value of the option","oneOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"}]}},"required":["text","value"],"type":"object"},"VariableRefresh":{"description":"Options to config when to refresh a variable\n` + "`" + `never` + "`" + `: Never refresh the variable\n` + "`" + `onDashboardLoad` + "`" + `: Queries the data source every time the dashboard loads.\n` + "`" + `onTimeRangeChanged` + "`" + `: Queries the data source when the dashboard time range changes.","enum":["never","onDashboardLoad","onTimeRangeChanged"],"type":"string"},"VariableRegexApplyTo":{"description":"Determine whether regex applies to variable value or display text\nAccepted values are ` + "`" + `value` + "`" + ` (apply to value used in queries) or ` + "`" + `text` + "`" + ` (apply to display text shown to users)","enum":["value","text"],"type":"string"},"VariableSort":{"description":"Sort variable options\nAccepted values are:\n` + "`" + `disabled` + "`" + `: No sorting\n` + "`" + `alphabeticalAsc` + "`" + `: Alphabetical ASC\n` + "`" + `alphabeticalDesc` + "`" + `: Alphabetical DESC\n` + "`" + `numericalAsc` + "`" + `: Numerical ASC\n` + "`" + `numericalDesc` + "`" + `: Numerical DESC\n` + "`" + `alphabeticalCaseInsensitiveAsc` + "`" + `: Alphabetical Case Insensitive ASC\n` + "`" + `alphabeticalCaseInsensitiveDesc` + "`" + `: Alphabetical Case Insensitive DESC\n` + "`" + `naturalAsc` + "`" + `: Natural ASC\n` + "`" + `naturalDesc` + "`" + `: Natural DESC\nVariableSort enum with default value","enum":["disabled","alphabeticalAsc","alphabeticalDesc","numericalAsc","numericalDesc","alphabeticalCaseInsensitiveAsc","alphabeticalCaseInsensitiveDesc","naturalAsc","naturalDesc"],"type":"string"},"VizConfigKind":{"additionalProperties":false,"properties":{"group":{"description":"The group is the plugin ID","type":"string"},"kind":{"const":"VizConfig","type":"string"},"spec":{"$ref":"#/components/schemas/VizConfigSpec"},"version":{"type":"string"}},"required":["kind","group","version","spec"],"type":"object"},"VizConfigSpec":{"additionalProperties":false,"description":"--- Kinds ---","properties":{"fieldConfig":{"$ref":"#/components/schemas/FieldConfigSource"},"options":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"}},"required":["options","fieldConfig"],"type":"object"},"spec":{"additionalProperties":false,"properties":{"annotations":{"items":{"$ref":"#/components/schemas/AnnotationQueryKind"},"type":"array"},"cursorSync":{"$ref":"#/components/schemas/DashboardCursorSync","default":"Off","description":"Configuration of dashboard cursor sync behavior.\n\"Off\" for no shared crosshair or tooltip (default).\n\"Crosshair\" for shared crosshair.\n\"Tooltip\" for shared crosshair AND shared tooltip."},"description":{"description":"Description of dashboard.","type":"string"},"editable":{"default":true,"description":"Whether a dashboard is editable or not.","type":"boolean"},"elements":{"additionalProperties":{"$ref":"#/components/schemas/Element"},"type":"object"},"layout":{"oneOf":[{"$ref":"#/components/schemas/GridLayoutKind"},{"$ref":"#/components/schemas/RowsLayoutKind"},{"$ref":"#/components/schemas/AutoGridLayoutKind"},{"$ref":"#/components/schemas/TabsLayoutKind"}]},"links":{"description":"Links with references to other dashboards or external websites.","items":{"$ref":"#/components/schemas/DashboardLink"},"type":"array"},"liveNow":{"description":"When set to true, the dashboard will redraw panels at an interval matching the pixel width.\nThis will keep data \"moving left\" regardless of the query refresh rate. This setting helps\navoid dashboards presenting stale live data.","type":"boolean"},"preload":{"default":false,"description":"When set to true, the dashboard will load all panels in the dashboard when it's loaded.","type":"boolean"},"revision":{"description":"Plugins only. The version of the dashboard installed together with the plugin.\nThis is used to determine if the dashboard should be updated when the plugin is updated.","type":"integer"},"tags":{"description":"Tags associated with dashboard.","items":{"type":"string"},"type":"array"},"timeSettings":{"$ref":"#/components/schemas/TimeSettingsSpec"},"title":{"description":"Title of dashboard.","type":"string"},"variables":{"description":"Configured template variables.","items":{"$ref":"#/components/schemas/VariableKind"},"type":"array"}},"required":["annotations","cursorSync","elements","layout","links","preload","tags","timeSettings","title","variables"],"type":"object"},"status":{"additionalProperties":false,"properties":{"conversion":{"$ref":"#/components/schemas/ConversionStatus","description":"Optional conversion status."}},"type":"object"}}`)
versionSchemaDashboardv2beta1 app.VersionSchema
_ = json.Unmarshal(rawSchemaDashboardv2beta1, &versionSchemaDashboardv2beta1)
)
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/annotations/v1beta1.annotation-filtering.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/annotations/v1beta1.annotation-filtering.v42.json
new file mode 100644
index 00000000000..5e3d12546e1
--- /dev/null
+++ b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/annotations/v1beta1.annotation-filtering.v42.json
@@ -0,0 +1,427 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "grafana",
+ "uid": "-- Grafana --"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations \u0026 Alerts",
+ "type": "dashboard"
+ },
+ {
+ "datasource": {
+ "type": "grafana",
+ "uid": "-- Grafana --"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations \u0026 Alerts",
+ "target": {
+ "limit": 100,
+ "matchAny": false,
+ "tags": [],
+ "type": "dashboard"
+ },
+ "type": "dashboard"
+ },
+ {
+ "datasource": {
+ "type": "grafana-testdata-datasource"
+ },
+ "enable": true,
+ "filter": {
+ "exclude": false,
+ "ids": [
+ 1
+ ]
+ },
+ "iconColor": "red",
+ "name": "Red, only panel 1",
+ "target": {
+ "lines": 4,
+ "refId": "Anno",
+ "scenarioId": "annotations"
+ }
+ },
+ {
+ "datasource": {
+ "type": "grafana-testdata-datasource"
+ },
+ "enable": true,
+ "filter": {
+ "exclude": true,
+ "ids": [
+ 1
+ ]
+ },
+ "iconColor": "yellow",
+ "name": "Yellow - all except 1",
+ "target": {
+ "lines": 5,
+ "refId": "Anno",
+ "scenarioId": "annotations"
+ }
+ },
+ {
+ "datasource": {
+ "type": "grafana-testdata-datasource"
+ },
+ "enable": true,
+ "filter": {
+ "exclude": false,
+ "ids": [
+ 3,
+ 4
+ ]
+ },
+ "iconColor": "dark-purple",
+ "name": "Purple only panel 3+4",
+ "target": {
+ "lines": 6,
+ "refId": "Anno",
+ "scenarioId": "annotations"
+ }
+ }
+ ]
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "id": 119,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "grafana-testdata-datasource"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": 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"
+ },
+ {
+ "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": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "Panel one",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "grafana-testdata-datasource"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": 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"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 0
+ },
+ "id": 2,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "Panel two",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "grafana-testdata-datasource"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": 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"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 0,
+ "y": 8
+ },
+ "id": 3,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "Panel three",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "grafana-testdata-datasource"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": 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"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 8,
+ "w": 12,
+ "x": 12,
+ "y": 8
+ },
+ "id": 4,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "title": "Panel four",
+ "type": "timeseries"
+ }
+ ],
+ "refresh": "",
+ "schemaVersion": 42,
+ "tags": [
+ "gdev",
+ "annotations"
+ ],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-30m",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "",
+ "title": "Annotation filtering",
+ "uid": "ed155665",
+ "weekStart": ""
+}
\ No newline at end of file
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/annotations/v1beta1.multi-lane-annotations.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/annotations/v1beta1.multi-lane-annotations.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_complex.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v1beta1.elasticsearch_complex.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_complex.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v1beta1.elasticsearch_complex.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_migration.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v1beta1.elasticsearch_migration.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_migration.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v1beta1.elasticsearch_migration.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_simple.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v1beta1.elasticsearch_simple.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v0alpha1.elasticsearch_simple.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-elasticsearch/v1beta1.elasticsearch_simple.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-logs.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-influxdb/v1beta1.influxdb-logs.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-logs.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-influxdb/v1beta1.influxdb-logs.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-templated.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-influxdb/v1beta1.influxdb-templated.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-influxdb/v0alpha1.influxdb-templated.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-influxdb/v1beta1.influxdb-templated.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_fakedata.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-loki/v1beta1.loki_fakedata.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_fakedata.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-loki/v1beta1.loki_fakedata.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-loki/v1beta1.loki_query_splitting.v42.json
similarity index 98%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-loki/v1beta1.loki_query_splitting.v42.json
index a7beffa4cdc..8af239195cb 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-loki/v1beta1.loki_query_splitting.v42.json
@@ -219,8 +219,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -312,8 +311,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -492,8 +490,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -584,8 +581,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -676,8 +672,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -791,8 +786,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -906,8 +900,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -1022,8 +1015,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_fakedata.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mssql/v1beta1.mssql_fakedata.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_fakedata.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mssql/v1beta1.mssql_fakedata.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_unittest.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mssql/v1beta1.mssql_unittest.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mssql/v0alpha1.mssql_unittest.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mssql/v1beta1.mssql_unittest.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_fakedata.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mysql/v1beta1.mysql_fakedata.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_fakedata.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mysql/v1beta1.mysql_fakedata.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_unittest.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mysql/v1beta1.mysql_unittest.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mysql/v0alpha1.mysql_unittest.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-mysql/v1beta1.mysql_unittest.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-opentsdb/v1beta1.opentsdb.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-opentsdb/v1beta1.opentsdb.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb_v23.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-opentsdb/v1beta1.opentsdb_v23.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-opentsdb/v0alpha1.opentsdb_v23.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-opentsdb/v1beta1.opentsdb_v23.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_fakedata.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-postgres/v1beta1.postgres_fakedata.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_fakedata.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-postgres/v1beta1.postgres_fakedata.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_unittest.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-postgres/v1beta1.postgres_unittest.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-postgres/v0alpha1.postgres_unittest.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-postgres/v1beta1.postgres_unittest.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v0alpha1.bar-gauge-demo2.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v1beta1.bar-gauge-demo2.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v0alpha1.bar-gauge-demo2.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v1beta1.bar-gauge-demo2.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v0alpha1.demo1.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v1beta1.demo1.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v0alpha1.demo1.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v1beta1.demo1.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v74.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v1beta1.new_features_in_v74.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v74.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v1beta1.new_features_in_v74.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v8.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v1beta1.new_features_in_v8.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v0alpha1.new_features_in_v8.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/datasource-testdata/v1beta1.new_features_in_v8.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-Kitchen-Sink.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-Kitchen-Sink.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-Kitchen-Sink.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-Kitchen-Sink.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-horizontally.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-a-panel-horizontally.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-horizontally.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-a-panel-horizontally.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-vertically.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-a-panel-vertically.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-panel-vertically.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-a-panel-vertically.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-a-row-with-a-repeating-horizontal-panel.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-vertical-panel.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-a-row-with-a-repeating-vertical-panel.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-a-row-with-a-repeating-vertical-panel.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-a-row-with-a-repeating-vertical-panel.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-an-empty-row.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-an-empty-row.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v0alpha1.Repeating-an-empty-row.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/e2e-repeats/v1beta1.Repeating-an-empty-row.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/extensions/v0alpha1.link-onclick-extensions.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/extensions/v1beta1.link-onclick-extensions.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/extensions/v0alpha1.link-onclick-extensions.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/extensions/v1beta1.link-onclick-extensions.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/extensions/v0alpha1.link-path-extensions.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/extensions/v1beta1.link-path-extensions.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/extensions/v0alpha1.link-path-extensions.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/extensions/v1beta1.link-path-extensions.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.datadata-macros.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.datadata-macros.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.datadata-macros.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.datadata-macros.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.global-variables-and-interpolation.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.global-variables-and-interpolation.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.global-variables-and-interpolation.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.global-variables-and-interpolation.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.templating-dashboard-links-and-variables.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.templating-dashboard-links-and-variables.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.templating-dashboard-links-and-variables.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.templating-dashboard-links-and-variables.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-panels.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.templating-repeating-panels.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-panels.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.templating-repeating-panels.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-rows.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.templating-repeating-rows.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.templating-repeating-rows.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.templating-repeating-rows.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.templating-textbox-e2e-scenarios.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.templating-textbox-e2e-scenarios.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.templating-textbox-e2e-scenarios.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.templating-textbox-e2e-scenarios.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-datalinks.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-datalinks.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-datalinks.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-datalinks.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables-drilldown.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-nested-variables-drilldown.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables-drilldown.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-nested-variables-drilldown.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-nested-variables.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-nested-variables.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-test-variable-output.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-test-variable-output.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-test-variable-output.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-test-variable-output.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-textbox.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-variables-textbox.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-textbox.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-variables-textbox.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-that-update-on-time-change.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-variables-that-update-on-time-change.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-variables-that-update-on-time-change.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/feature-templating/v1beta1.testdata-variables-that-update-on-time-change.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v0alpha1.live-flakey-refresh.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v1beta1.live-flakey-refresh.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v0alpha1.live-flakey-refresh.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v1beta1.live-flakey-refresh.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v0alpha1.live-flakey.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v1beta1.live-flakey.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v0alpha1.live-flakey.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v1beta1.live-flakey.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v0alpha1.live-publish.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v1beta1.live-publish.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v0alpha1.live-publish.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v1beta1.live-publish.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v0alpha1.live-streams.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v1beta1.live-streams.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v0alpha1.live-streams.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/live/v1beta1.live-streams.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/migrations/v0alpha1.migrations.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/migrations/v1beta1.migrations.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/migrations/v0alpha1.migrations.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/migrations/v1beta1.migrations.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-autosizing.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v1beta1.barchart-autosizing.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-autosizing.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v1beta1.barchart-autosizing.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-label-rotation-skipping.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v1beta1.barchart-label-rotation-skipping.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-label-rotation-skipping.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v1beta1.barchart-label-rotation-skipping.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-series-toggle.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v1beta1.barchart-series-toggle.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-series-toggle.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v1beta1.barchart-series-toggle.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-thresholds-mappings.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v1beta1.barchart-thresholds-mappings.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-thresholds-mappings.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v1beta1.barchart-thresholds-mappings.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-tooltips-legends.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v1beta1.barchart-tooltips-legends.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v0alpha1.barchart-tooltips-legends.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-barchart/v1beta1.barchart-tooltips-legends.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v0alpha1.bar_gauge_demo.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v1beta1.bar_gauge_demo.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v0alpha1.bar_gauge_demo.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v1beta1.bar_gauge_demo.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v1beta1.panel_tests_bar_gauge.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v1beta1.panel_tests_bar_gauge.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge2.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v1beta1.panel_tests_bar_gauge2.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v0alpha1.panel_tests_bar_gauge2.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-bargauge/v1beta1.panel_tests_bar_gauge2.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-candlestick/v0alpha1.candlestick.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-candlestick/v1beta1.candlestick.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-candlestick/v0alpha1.candlestick.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-candlestick/v1beta1.candlestick.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-connection-examples.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v1beta1.canvas-connection-examples.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-connection-examples.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v1beta1.canvas-connection-examples.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-datalinks.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v1beta1.canvas-datalinks.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-datalinks.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v1beta1.canvas-datalinks.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-examples.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v1beta1.canvas-examples.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v0alpha1.canvas-examples.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-canvas/v1beta1.canvas-examples.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.auto_decimals.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.auto_decimals.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.auto_decimals.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.auto_decimals.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.color_modes.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.color_modes.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.color_modes.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.color_modes.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.lazy_loading.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.lazy_loading.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.lazy_loading.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.lazy_loading.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.linked-viz.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.linked-viz.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.linked-viz.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.linked-viz.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.panels_without_title.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.panels_without_title.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.panels_without_title.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.panels_without_title.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.shared_queries.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.shared_queries.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v0alpha1.shared_queries.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-common/v1beta1.shared_queries.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-dashlist/v0alpha1.dashlist.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-dashlist/v1beta1.dashlist.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-dashlist/v0alpha1.dashlist.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-dashlist/v1beta1.dashlist.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-datagrid/v0alpha1.datagrid_metric_values.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-datagrid/v1beta1.datagrid_metric_values.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-datagrid/v0alpha1.datagrid_metric_values.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-datagrid/v1beta1.datagrid_metric_values.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-flamegraph/v0alpha1.panel_tests_flame_graph.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-flamegraph/v1beta1.panel_tests_flame_graph.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-flamegraph/v0alpha1.panel_tests_flame_graph.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-flamegraph/v1beta1.panel_tests_flame_graph.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge-multi-series.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v1beta1.gauge-multi-series.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge-multi-series.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v1beta1.gauge-multi-series.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v1beta1.gauge_tests.v42.json
similarity index 93%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v1beta1.gauge_tests.v42.json
index 92a865b0b10..b00b08dd2ab 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests.v42.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v1beta1.gauge_tests.v42.json
@@ -65,17 +65,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -136,17 +133,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -207,17 +201,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -271,7 +262,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -279,17 +269,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -342,7 +329,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -350,17 +336,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -414,7 +397,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -422,17 +404,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -485,7 +464,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -493,17 +471,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -668,7 +643,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"options": {
@@ -685,17 +659,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
- "color": "#e24d42",
- "index": 2,
+ "color": "#e24d42",
"value": 90
}
]
@@ -750,7 +721,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"options": {
@@ -768,17 +738,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -833,7 +800,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"options": {
@@ -852,17 +818,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -917,7 +880,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"options": {
@@ -946,17 +908,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -1038,7 +997,7 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "2",
+ "decimals": 2,
"mappings": [],
"max": 100,
"min": 0,
@@ -1046,17 +1005,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v1beta1.gauge_tests_new.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_new.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v1beta1.gauge_tests_new.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v1beta1.gauge_tests_old_to_new.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v0alpha1.gauge_tests_old_to_new.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-gauge/v1beta1.gauge_tests_old_to_new.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-color-field.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap-color-field.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-color-field.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap-color-field.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-photo-layer.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap-photo-layer.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-photo-layer.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap-photo-layer.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-route-layer.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap-route-layer.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-route-layer.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap-route-layer.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-spatial-operations-transformer.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap-spatial-operations-transformer.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-spatial-operations-transformer.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap-spatial-operations-transformer.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-v91.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap-v91.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap-v91.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap-v91.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap_multi-layers.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap_multi-layers.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.geomap_multi-layers.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.geomap_multi-layers.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.panel-geomap.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.panel-geomap.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v0alpha1.panel-geomap.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-geomap/v1beta1.panel-geomap.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v0alpha1.graph-gradient-area-fills.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v1beta1.graph-gradient-area-fills.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v0alpha1.graph-gradient-area-fills.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v1beta1.graph-gradient-area-fills.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v0alpha1.graph-shared-tooltips.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v1beta1.graph-shared-tooltips.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v0alpha1.graph-shared-tooltips.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v1beta1.graph-shared-tooltips.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v0alpha1.graph-time-regions.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v1beta1.graph-time-regions.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v0alpha1.graph-time-regions.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v1beta1.graph-time-regions.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v0alpha1.graph_tests.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v1beta1.graph_tests.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v0alpha1.graph_tests.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v1beta1.graph_tests.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v0alpha1.graph_y_axis.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v1beta1.graph_y_axis.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v0alpha1.graph_y_axis.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-graph/v1beta1.graph_y_axis.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-calculate-log.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v1beta1.heatmap-calculate-log.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-calculate-log.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v1beta1.heatmap-calculate-log.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-legacy.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v1beta1.heatmap-legacy.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-legacy.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v1beta1.heatmap-legacy.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v1beta1.heatmap-x.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-heatmap/v1beta1.heatmap-x.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-histogram/v1beta1.histogram_tests.v42.json
similarity index 99%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-histogram/v1beta1.histogram_tests.v42.json
index 7e392bd55d0..5b3e0ed0b72 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-histogram/v1beta1.histogram_tests.v42.json
@@ -58,8 +58,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -127,8 +126,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -196,8 +194,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -277,8 +274,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -355,8 +351,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -448,8 +443,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -536,8 +530,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -619,8 +612,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -702,8 +694,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -785,8 +776,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -850,8 +840,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-library/v0alpha1.panel-library.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-library/v1beta1.panel-library.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-library/v0alpha1.panel-library.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-library/v1beta1.panel-library.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-piechart/v0alpha1.panel_test_piechart.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-piechart/v1beta1.panel_test_piechart.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-piechart/v0alpha1.panel_test_piechart.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-piechart/v1beta1.panel_test_piechart.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-polystat/v0alpha1.polystat_test.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-polystat/v1beta1.polystat_test.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-polystat/v0alpha1.polystat_test.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-polystat/v1beta1.polystat_test.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-stat/v0alpha1.panel-stat-tests.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-stat/v1beta1.panel-stat-tests.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-stat/v0alpha1.panel-stat-tests.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-stat/v1beta1.panel-stat-tests.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-status-history/v1beta1.status-history-thresholds-mappings.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-status-history/v1beta1.status-history-thresholds-mappings.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_footer.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_footer.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_kitchen_sink.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_kitchen_sink.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_markdown.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_markdown.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_markdown.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_markdown.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_pagination.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_pagination.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_pagination.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_pagination.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_sparkline_cell.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_sparkline_cell.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_tests.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_tests.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_tests.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_tests.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_tests_new.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_tests_new.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_tests_new.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_tests_new.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_v12_2_migrations.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-table/v1beta1.table_v12_2_migrations.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-text/v0alpha1.text-options.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-text/v1beta1.text-options.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-text/v0alpha1.text-options.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-text/v1beta1.text-options.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-endtime.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v1beta1.timeline-align-endtime.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-endtime.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v1beta1.timeline-align-endtime.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-nulls-retain.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v1beta1.timeline-align-nulls-retain.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-align-nulls-retain.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v1beta1.timeline-align-nulls-retain.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-demo.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v1beta1.timeline-demo.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-demo.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v1beta1.timeline-demo.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-modes.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v1beta1.timeline-modes.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-modes.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v1beta1.timeline-modes.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v1beta1.timeline-thresholds-mappings.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeline/v1beta1.timeline-thresholds-mappings.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-bars-high-density.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-bars-high-density.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-bars-high-density.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-bars-high-density.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-by-value-color-schemes.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-by-value-color-schemes.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-by-value-color-schemes.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-by-value-color-schemes.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-formats.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-formats.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-formats.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-formats.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-gradient-area.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-gradient-area.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-hue-gradients.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-hue-gradients.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-nulls.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-nulls.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-nulls.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-nulls.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-out-of-rage.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-out-of-rage.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-out-of-rage.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-out-of-rage.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-shared-tooltip-cursor-position.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-shared-tooltip-cursor-position.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-shared-tooltip-cursor-position.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-shared-tooltip-cursor-position.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-soft-limits.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-soft-limits.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-soft-limits.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-soft-limits.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-stacking.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-stacking.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking2.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-stacking2.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-stacking2.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-stacking2.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-thresholds.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-thresholds.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-thresholds.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-thresholds.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-time.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-time.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-time.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-time.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-y-ticks-zero-decimals.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-y-ticks-zero-decimals.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-y-ticks-zero-decimals.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-y-ticks-zero-decimals.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-yaxis-ticks.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-yaxis-ticks.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-yaxis-ticks.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries-yaxis-ticks.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-timeseries/v1beta1.timeseries.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-trend/v0alpha1.trend_example.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-trend/v1beta1.trend_example.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-trend/v0alpha1.trend_example.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-trend/v1beta1.trend_example.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-demo.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v1beta1.xychart-demo.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-demo.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v1beta1.xychart-demo.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-migrations.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v1beta1.xychart-migrations.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-migrations.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v1beta1.xychart-migrations.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v1beta1.xychart-tooltip-color-test.v42.json
similarity index 98%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v1beta1.xychart-tooltip-color-test.v42.json
index 417ea1661e1..f28fee864e5 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/panel-xychart/v1beta1.xychart-tooltip-color-test.v42.json
@@ -61,8 +61,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -148,8 +147,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -235,8 +233,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -322,8 +319,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -416,8 +412,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -510,8 +505,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -604,8 +598,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v0alpha1.mostly-blank-dashboard.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v1beta1.mostly-blank-dashboard.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v0alpha1.mostly-blank-dashboard.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v1beta1.mostly-blank-dashboard.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v0alpha1.relative_time_zone_support.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v1beta1.relative_time_zone_support.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v0alpha1.relative_time_zone_support.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v1beta1.relative_time_zone_support.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v0alpha1.slow_queries_and_annotations.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v1beta1.slow_queries_and_annotations.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v0alpha1.slow_queries_and_annotations.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v1beta1.slow_queries_and_annotations.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v0alpha1.tall_dashboard.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v1beta1.tall_dashboard.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v0alpha1.tall_dashboard.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v1beta1.tall_dashboard.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v0alpha1.time_zone_support.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v1beta1.time_zone_support.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v0alpha1.time_zone_support.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/scenarios/v1beta1.time_zone_support.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.config-from-query.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.config-from-query.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.config-from-query.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.config-from-query.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.extract-json-paths.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.extract-json-paths.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.extract-json-paths.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.extract-json-paths.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.filter.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.filter.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.join-by-field.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.join-by-field.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.join-by-field.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.join-by-field.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.join-by-labels.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.join-by-labels.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.join-by-labels.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.join-by-labels.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.regression-analysis.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.regression-analysis.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.regression-analysis.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.regression-analysis.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.reuse.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.reuse.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.reuse.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.reuse.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.rows-to-fields.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.rows-to-fields.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v0alpha1.rows-to-fields.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/transforms/v1beta1.rows-to-fields.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.all-panels.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/v1beta1.v1beta1.v1beta1.all-panels.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.all-panels.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/v1beta1.v1beta1.v1beta1.all-panels.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.home.v42.json b/apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/v1beta1.v1beta1.v1beta1.home.v42.json
similarity index 100%
rename from apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/v0alpha1.v1beta1.v1beta1.home.v42.json
rename to apps/dashboard/pkg/migration/conversion/testdata/input/migrated_dev_dashboards/v1beta1.v1beta1.v1beta1.home.v42.json
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2alpha1.json
index b6addcc81ed..d6f207c6fdd 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2alpha1.json
@@ -970,8 +970,7 @@
"le": 1e-9
},
"legend": {
- "show": true,
- "showLegend": true
+ "show": true
},
"rowsFrame": {
"layout": "auto"
@@ -1064,8 +1063,7 @@
"le": 1e-9
},
"legend": {
- "show": true,
- "showLegend": true
+ "show": true
},
"rowsFrame": {
"layout": "auto"
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2beta1.json
index f806e27a98f..c7ef28fa2b8 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/annotations/v0alpha1.multi-lane-annotations.v42.v2beta1.json
@@ -991,8 +991,7 @@
"le": 1e-9
},
"legend": {
- "show": true,
- "showLegend": true
+ "show": true
},
"rowsFrame": {
"layout": "auto"
@@ -1087,8 +1086,7 @@
"le": 1e-9
},
"legend": {
- "show": true,
- "showLegend": true
+ "show": true
},
"rowsFrame": {
"layout": "auto"
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v1beta1.json
index aed8292522f..3cb966ba891 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v1beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/datasource-loki/v0alpha1.loki_query_splitting.v42.v1beta1.json
@@ -225,8 +225,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -318,8 +317,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -498,8 +496,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -590,8 +587,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -682,8 +678,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -797,8 +792,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -912,8 +906,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -1028,8 +1021,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2alpha1.json
index b1dbd3de041..4d208a1d8dc 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2alpha1.json
@@ -467,7 +467,8 @@
"title": "Go to drilldown",
"url": "/d/O6GmNPvWk/dashboard-tests-nested-template-variables-drilldown?orgId=1\u0026${__all_variables}\u0026${__url_time_range}"
}
- ]
+ ],
+ "nullValueMode": "connected"
},
"overrides": []
}
@@ -550,7 +551,8 @@
"title": "Go to drilldown",
"url": "/d/O6GmNPvWk/dashboard-tests-nested-template-variables-drilldown?orgId=1\u0026${__all_variables}\u0026${__url_time_range}"
}
- ]
+ ],
+ "nullValueMode": "connected"
},
"overrides": []
}
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2beta1.json
index 9089dd1d1fb..5165f97554d 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/feature-templating/v0alpha1.testdata-nested-variables.v42.v2beta1.json
@@ -481,7 +481,8 @@
"title": "Go to drilldown",
"url": "/d/O6GmNPvWk/dashboard-tests-nested-template-variables-drilldown?orgId=1\u0026${__all_variables}\u0026${__url_time_range}"
}
- ]
+ ],
+ "nullValueMode": "connected"
},
"overrides": []
}
@@ -566,7 +567,8 @@
"title": "Go to drilldown",
"url": "/d/O6GmNPvWk/dashboard-tests-nested-template-variables-drilldown?orgId=1\u0026${__all_variables}\u0026${__url_time_range}"
}
- ]
+ ],
+ "nullValueMode": "connected"
},
"overrides": []
}
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2alpha1.json
index 2eb67e36f2f..a1b9c4b230a 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2alpha1.json
@@ -169,8 +169,7 @@
"le": 1e-9
},
"legend": {
- "show": true,
- "showLegend": true
+ "show": true
},
"rowsFrame": {
"layout": "auto"
@@ -336,8 +335,7 @@
"le": 1e-9
},
"legend": {
- "show": true,
- "showLegend": true
+ "show": true
},
"rowsFrame": {
"layout": "auto"
@@ -408,8 +406,7 @@
"le": 1e-9
},
"legend": {
- "show": true,
- "showLegend": true
+ "show": true
},
"rowsFrame": {
"layout": "auto"
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2beta1.json
index acba4cedbc2..2428d6fd107 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-heatmap/v0alpha1.heatmap-x.v42.v2beta1.json
@@ -175,8 +175,7 @@
"le": 1e-9
},
"legend": {
- "show": true,
- "showLegend": true
+ "show": true
},
"rowsFrame": {
"layout": "auto"
@@ -347,8 +346,7 @@
"le": 1e-9
},
"legend": {
- "show": true,
- "showLegend": true
+ "show": true
},
"rowsFrame": {
"layout": "auto"
@@ -420,8 +418,7 @@
"le": 1e-9
},
"legend": {
- "show": true,
- "showLegend": true
+ "show": true
},
"rowsFrame": {
"layout": "auto"
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v1beta1.json
index b65608bc758..c3435f0f17d 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v1beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v1beta1.json
@@ -64,8 +64,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -133,8 +132,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -202,8 +200,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -283,8 +280,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -361,8 +357,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -454,8 +449,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -542,8 +536,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -625,8 +618,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -708,8 +700,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -791,8 +782,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -856,8 +846,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2alpha1.json
index 57fbcad9d99..ec5c52a7119 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2alpha1.json
@@ -882,6 +882,7 @@
"kind": "filterFieldsByName",
"spec": {
"id": "filterFieldsByName",
+ "disabled": true,
"options": {
"include": {
"names": [
@@ -895,6 +896,7 @@
"kind": "histogram",
"spec": {
"id": "histogram",
+ "disabled": true,
"options": {
"combine": true,
"fields": {}
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2beta1.json
index 5b2ee8d8df2..3ff41469dbc 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-histogram/v0alpha1.histogram_tests.v42.v2beta1.json
@@ -911,6 +911,7 @@
"kind": "filterFieldsByName",
"spec": {
"id": "filterFieldsByName",
+ "disabled": true,
"options": {
"include": {
"names": [
@@ -924,6 +925,7 @@
"kind": "histogram",
"spec": {
"id": "histogram",
+ "disabled": true,
"options": {
"combine": true,
"fields": {}
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2alpha1.json
index d90c8dc52cd..b9ba0b13da4 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2alpha1.json
@@ -222,7 +222,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -318,7 +319,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -424,7 +426,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -507,7 +510,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2beta1.json
index 7aaa0fff33a..e130fc7e172 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-status-history/v0alpha1.status-history-thresholds-mappings.v42.v2beta1.json
@@ -229,7 +229,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -328,7 +329,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -437,7 +439,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -523,7 +526,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2alpha1.json
index 540f0d9e54d..9d15475c82d 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2alpha1.json
@@ -167,7 +167,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -361,7 +362,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -596,7 +598,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -787,7 +790,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -974,7 +978,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1181,7 +1186,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1384,7 +1390,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1573,7 +1580,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2beta1.json
index 6c9aa023163..3965312c00a 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_footer.v42.v2beta1.json
@@ -173,7 +173,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -372,7 +373,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -612,7 +614,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -808,7 +811,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1000,7 +1004,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1212,7 +1217,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1420,7 +1426,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1614,7 +1621,8 @@
},
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2alpha1.json
index bccce10d162..f342cab8373 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2alpha1.json
@@ -194,7 +194,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1190,7 +1191,8 @@
"reducer": []
},
"inspect": true
- }
+ },
+ "fieldMinMax": true
},
"overrides": []
}
@@ -1262,7 +1264,8 @@
"type": "auto"
},
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1418,7 +1421,8 @@
"type": "auto"
},
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1575,7 +1579,8 @@
"type": "auto"
},
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1737,7 +1742,8 @@
"type": "auto"
},
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1888,7 +1894,8 @@
"type": "auto"
},
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2beta1.json
index 5e186ef1443..59f9b3d7942 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_kitchen_sink.v42.v2beta1.json
@@ -200,7 +200,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1208,7 +1209,8 @@
"reducer": []
},
"inspect": true
- }
+ },
+ "fieldMinMax": true
},
"overrides": []
}
@@ -1283,7 +1285,8 @@
"type": "auto"
},
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1442,7 +1445,8 @@
"type": "auto"
},
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1602,7 +1606,8 @@
"type": "auto"
},
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1767,7 +1772,8 @@
"type": "auto"
},
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1921,7 +1927,8 @@
"type": "auto"
},
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2alpha1.json
index e5e260fd150..92729fdddcb 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2alpha1.json
@@ -302,6 +302,23 @@
"url": "https://google.com/search?q=grafana"
}
],
+ "actions": [
+ {
+ "type": "fetch",
+ "title": "Get instance health",
+ "fetch": {
+ "method": "GET",
+ "url": "/api/health",
+ "body": "{}",
+ "headers": [
+ [
+ "Content-Type",
+ "application/json"
+ ]
+ ]
+ }
+ }
+ ],
"custom": {
"align": "auto",
"cellOptions": {
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2beta1.json
index ac15a298939..5246af0a06b 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_sparkline_cell.v42.v2beta1.json
@@ -312,6 +312,23 @@
"url": "https://google.com/search?q=grafana"
}
],
+ "actions": [
+ {
+ "type": "fetch",
+ "title": "Get instance health",
+ "fetch": {
+ "method": "GET",
+ "url": "/api/health",
+ "body": "{}",
+ "headers": [
+ [
+ "Content-Type",
+ "application/json"
+ ]
+ ]
+ }
+ }
+ ],
"custom": {
"align": "auto",
"cellOptions": {
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2alpha1.json
index 1b6348c35d5..d6451b5d80f 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2alpha1.json
@@ -206,7 +206,8 @@
},
"filterable": true,
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -753,7 +754,8 @@
},
"filterable": true,
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1303,7 +1305,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1497,7 +1500,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1692,7 +1696,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1886,7 +1891,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -2081,7 +2087,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -2276,7 +2283,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2beta1.json
index a77140c5beb..75353a995ac 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-table/v0alpha1.table_v12_2_migrations.v42.v2beta1.json
@@ -212,7 +212,8 @@
},
"filterable": true,
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -764,7 +765,8 @@
},
"filterable": true,
"inspect": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1319,7 +1321,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1518,7 +1521,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1718,7 +1722,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -1917,7 +1922,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -2117,7 +2123,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
@@ -2317,7 +2324,8 @@
"filterable": true,
"inspect": false,
"wrapText": false
- }
+ },
+ "fieldMinMax": true
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2alpha1.json
index 8dff3c34ccf..3366490c00a 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2alpha1.json
@@ -222,7 +222,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -318,7 +319,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -424,7 +426,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -507,7 +510,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2beta1.json
index 3baa3d21130..59d2e929972 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeline/v0alpha1.timeline-thresholds-mappings.v42.v2beta1.json
@@ -229,7 +229,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -328,7 +329,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -437,7 +439,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -523,7 +526,8 @@
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2alpha1.json
index 5b43876c65f..bde73320d42 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2alpha1.json
@@ -110,7 +110,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -217,7 +218,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -324,7 +326,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -431,7 +434,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -537,7 +541,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -643,7 +648,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2beta1.json
index ca96f9d5720..06331f32233 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-gradient-area.v42.v2beta1.json
@@ -114,7 +114,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -223,7 +224,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -332,7 +334,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -441,7 +444,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -549,7 +553,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -657,7 +662,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2alpha1.json
index 74cba148009..c4bb5720d36 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2alpha1.json
@@ -116,7 +116,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -229,7 +230,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -342,7 +344,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -455,7 +458,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -568,7 +572,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -681,7 +686,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -794,7 +800,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -907,7 +914,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -1020,7 +1028,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2beta1.json
index 7e64bc79ef3..7c18be27f07 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries-hue-gradients.v42.v2beta1.json
@@ -120,7 +120,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -235,7 +236,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -350,7 +352,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -465,7 +468,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -580,7 +584,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -695,7 +700,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -810,7 +816,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -925,7 +932,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -1040,7 +1048,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2alpha1.json
index e50b453076a..cb63e4f234d 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2alpha1.json
@@ -3607,7 +3607,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -3740,7 +3741,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2beta1.json
index 65105663c85..a57e430cc63 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-timeseries/v0alpha1.timeseries.v42.v2beta1.json
@@ -3674,7 +3674,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
@@ -3809,7 +3810,8 @@
},
"showPoints": "never",
"spanNulls": true
- }
+ },
+ "nullValueMode": "null"
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v1beta1.json
index cdc93bf3cfa..1b7c9effbe8 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v1beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v1beta1.json
@@ -67,8 +67,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -154,8 +153,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -241,8 +239,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -328,8 +325,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -422,8 +418,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -516,8 +511,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -610,8 +604,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2alpha1.json
index 1d60f0ef9bf..861c4b41a6c 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2alpha1.json
@@ -124,7 +124,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -225,7 +226,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -326,7 +328,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -434,7 +437,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -542,7 +546,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -650,7 +655,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2beta1.json
index 5a46646474d..0ae6dc172bd 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/panel-xychart/v0alpha1.xychart-tooltip-color-test.v42.v2beta1.json
@@ -128,7 +128,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -232,7 +233,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -336,7 +338,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -447,7 +450,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -558,7 +562,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
@@ -669,7 +674,8 @@
"type": "linear"
},
"show": "points"
- }
+ },
+ "fieldMinMax": false
},
"overrides": []
}
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2alpha1.json
index 056fdc62383..dd3ba7146e5 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2alpha1.json
@@ -81,6 +81,10 @@
"kind": "reduce",
"spec": {
"id": "reduce",
+ "filter": {
+ "id": "byRefId",
+ "options": "A"
+ },
"options": {
"includeTimeField": false,
"mode": "reduceFields",
@@ -94,6 +98,10 @@
"kind": "reduce",
"spec": {
"id": "reduce",
+ "filter": {
+ "id": "byRefId",
+ "options": "B"
+ },
"options": {
"includeTimeField": false,
"mode": "reduceFields",
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2beta1.json
index 57c5559add1..0f4ab69c96a 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/migrated_dev_dashboards/transforms/v0alpha1.filter.v42.v2beta1.json
@@ -86,6 +86,10 @@
"kind": "reduce",
"spec": {
"id": "reduce",
+ "filter": {
+ "id": "byRefId",
+ "options": "A"
+ },
"options": {
"includeTimeField": false,
"mode": "reduceFields",
@@ -99,6 +103,10 @@
"kind": "reduce",
"spec": {
"id": "reduce",
+ "filter": {
+ "id": "byRefId",
+ "options": "B"
+ },
"options": {
"includeTimeField": false,
"mode": "reduceFields",
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2beta1.tabs-and-rows-repeated.v0alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2beta1.tabs-and-rows-repeated.v0alpha1.json
index 716b476f825..40b4fff030e 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2beta1.tabs-and-rows-repeated.v0alpha1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2beta1.tabs-and-rows-repeated.v0alpha1.json
@@ -586,6 +586,7 @@
},
"id": -1,
"panels": [],
+ "repeat": "custom_var_tab",
"title": "Repeated Tab by \"$custom_var_tab\"",
"type": "row"
},
@@ -610,8 +611,11 @@
"y": 22
},
"id": 6,
+ "maxPerRow": 3,
"options": {},
"pluginVersion": "12.4.0-19736337744",
+ "repeat": "custom_var_panel",
+ "repeatDirection": "h",
"targets": [
{
"refId": "A"
diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2beta1.tabs-and-rows-repeated.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2beta1.tabs-and-rows-repeated.v1beta1.json
index f915142fd14..ff1e2d42e20 100644
--- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2beta1.tabs-and-rows-repeated.v1beta1.json
+++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2beta1.tabs-and-rows-repeated.v1beta1.json
@@ -586,6 +586,7 @@
},
"id": -1,
"panels": [],
+ "repeat": "custom_var_tab",
"title": "Repeated Tab by \"$custom_var_tab\"",
"type": "row"
},
@@ -610,8 +611,11 @@
"y": 22
},
"id": 6,
+ "maxPerRow": 3,
"options": {},
"pluginVersion": "12.4.0-19736337744",
+ "repeat": "custom_var_panel",
+ "repeatDirection": "h",
"targets": [
{
"refId": "A"
diff --git a/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1.go b/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1.go
index 3eeb61893fa..bfff3c49797 100644
--- a/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1.go
+++ b/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1.go
@@ -2230,6 +2230,20 @@ func transformPanelTransformations(panelMap map[string]interface{}) []dashv2alph
Options: options,
},
}
+
+ // Extract disabled if present (optional, transformations are enabled by default)
+ if disabled, ok := tMap["disabled"].(bool); ok && disabled {
+ transformationKind.Spec.Disabled = &disabled
+ }
+
+ // Extract filter if present (optional frame matcher for transformations)
+ if filterMap, ok := tMap["filter"].(map[string]interface{}); ok {
+ transformationKind.Spec.Filter = &dashv2alpha1.DashboardMatcherConfig{
+ Id: schemaversion.GetStringValue(filterMap, "id"),
+ Options: filterMap["options"],
+ }
+ }
+
result = append(result, transformationKind)
}
}
@@ -2349,14 +2363,6 @@ func buildVizConfig(panelMap map[string]interface{}) dashv2alpha1.DashboardVizCo
}
}
- // Add frontend-style default options to match frontend behavior
- if legend, ok := options["legend"].(map[string]interface{}); ok {
- // Add showLegend: true to match frontend behavior
- showLegend := getBoolField(legend, "showLegend", true)
- legend["showLegend"] = showLegend
- options["legend"] = legend
- }
-
// Handle Angular panel migrations
// This replicates the v0→v1 migration logic for panels that weren't migrated yet.
// We check two cases:
@@ -2531,6 +2537,15 @@ func extractFieldConfigDefaults(defaults map[string]interface{}) dashv2alpha1.Da
fieldConfigDefaults.Writeable = val
hasDefaults = true
}
+ if val, ok := extractBoolField(defaults, "fieldMinMax"); ok {
+ fieldConfigDefaults.FieldMinMax = val
+ hasDefaults = true
+ }
+ if val, ok := defaults["nullValueMode"].(string); ok {
+ nullValueMode := dashv2alpha1.DashboardNullValueMode(val)
+ fieldConfigDefaults.NullValueMode = &nullValueMode
+ hasDefaults = true
+ }
// Extract array field - strip BOMs from link URLs
if linksArray, ok := extractArrayField(defaults, "links"); ok {
@@ -2543,6 +2558,12 @@ func extractFieldConfigDefaults(defaults map[string]interface{}) dashv2alpha1.Da
hasDefaults = true
}
+ // Extract actions array
+ if actionsArray, ok := extractArrayField(defaults, "actions"); ok {
+ fieldConfigDefaults.Actions = convertActionsToV2(actionsArray)
+ hasDefaults = true
+ }
+
// Extract mappings
if mappings, exists := defaults["mappings"]; exists {
resultMappings := buildValueMappings(mappings)
@@ -2842,6 +2863,157 @@ func extractFieldConfigOverrides(fieldConfig map[string]interface{}) []dashv2alp
return result
}
+// convertActionsToV2 converts an array of V1 action objects to V2 DashboardAction structs.
+func convertActionsToV2(actionsArray []interface{}) []dashv2alpha1.DashboardAction {
+ if len(actionsArray) == 0 {
+ return nil
+ }
+
+ result := make([]dashv2alpha1.DashboardAction, 0, len(actionsArray))
+ for _, action := range actionsArray {
+ actionMap, ok := action.(map[string]interface{})
+ if !ok {
+ continue
+ }
+
+ dashAction := dashv2alpha1.DashboardAction{
+ Type: dashv2alpha1.DashboardActionType(schemaversion.GetStringValue(actionMap, "type")),
+ Title: schemaversion.GetStringValue(actionMap, "title"),
+ }
+
+ // Convert confirmation
+ if confirmation, ok := actionMap["confirmation"].(string); ok && confirmation != "" {
+ dashAction.Confirmation = &confirmation
+ }
+
+ // Convert oneClick
+ if oneClick, ok := actionMap["oneClick"].(bool); ok {
+ dashAction.OneClick = &oneClick
+ }
+
+ // Convert fetch options
+ if fetchMap, ok := actionMap["fetch"].(map[string]interface{}); ok {
+ dashAction.Fetch = convertFetchOptionsToV2(fetchMap)
+ }
+
+ // Convert infinity options
+ if infinityMap, ok := actionMap["infinity"].(map[string]interface{}); ok {
+ dashAction.Infinity = convertInfinityOptionsToV2(infinityMap)
+ }
+
+ // Convert variables
+ if variablesArray, ok := actionMap["variables"].([]interface{}); ok {
+ dashAction.Variables = convertActionVariablesToV2(variablesArray)
+ }
+
+ // Convert style
+ if styleMap, ok := actionMap["style"].(map[string]interface{}); ok {
+ dashAction.Style = convertActionStyleToV2(styleMap)
+ }
+
+ result = append(result, dashAction)
+ }
+
+ return result
+}
+
+func convertFetchOptionsToV2(fetchMap map[string]interface{}) *dashv2alpha1.DashboardFetchOptions {
+ fetchOptions := &dashv2alpha1.DashboardFetchOptions{
+ Method: dashv2alpha1.DashboardHttpRequestMethod(schemaversion.GetStringValue(fetchMap, "method")),
+ Url: schemaversion.GetStringValue(fetchMap, "url"),
+ }
+
+ if body, ok := fetchMap["body"].(string); ok {
+ fetchOptions.Body = &body
+ }
+
+ // Convert queryParams (2D array of strings) - preserve empty arrays
+ if queryParams, ok := fetchMap["queryParams"].([]interface{}); ok {
+ fetchOptions.QueryParams = convert2DStringArrayPreserveEmpty(queryParams)
+ }
+
+ // Convert headers (2D array of strings) - preserve empty arrays
+ if headers, ok := fetchMap["headers"].([]interface{}); ok {
+ fetchOptions.Headers = convert2DStringArrayPreserveEmpty(headers)
+ }
+
+ return fetchOptions
+}
+
+func convertInfinityOptionsToV2(infinityMap map[string]interface{}) *dashv2alpha1.DashboardInfinityOptions {
+ infinityOptions := &dashv2alpha1.DashboardInfinityOptions{
+ Method: dashv2alpha1.DashboardHttpRequestMethod(schemaversion.GetStringValue(infinityMap, "method")),
+ Url: schemaversion.GetStringValue(infinityMap, "url"),
+ DatasourceUid: schemaversion.GetStringValue(infinityMap, "datasourceUid"),
+ }
+
+ if body, ok := infinityMap["body"].(string); ok {
+ infinityOptions.Body = &body
+ }
+
+ if queryParams, ok := infinityMap["queryParams"].([]interface{}); ok {
+ infinityOptions.QueryParams = convert2DStringArrayPreserveEmpty(queryParams)
+ }
+
+ if headers, ok := infinityMap["headers"].([]interface{}); ok {
+ infinityOptions.Headers = convert2DStringArrayPreserveEmpty(headers)
+ }
+
+ return infinityOptions
+}
+
+func convertActionVariablesToV2(variablesArray []interface{}) []dashv2alpha1.DashboardActionVariable {
+ if len(variablesArray) == 0 {
+ return nil
+ }
+
+ result := make([]dashv2alpha1.DashboardActionVariable, 0, len(variablesArray))
+ for _, variable := range variablesArray {
+ variableMap, ok := variable.(map[string]interface{})
+ if !ok {
+ continue
+ }
+
+ result = append(result, dashv2alpha1.DashboardActionVariable{
+ Key: schemaversion.GetStringValue(variableMap, "key"),
+ Name: schemaversion.GetStringValue(variableMap, "name"),
+ Type: schemaversion.GetStringValue(variableMap, "type"),
+ })
+ }
+
+ return result
+}
+
+func convertActionStyleToV2(styleMap map[string]interface{}) *dashv2alpha1.DashboardV2alpha1ActionStyle {
+ style := &dashv2alpha1.DashboardV2alpha1ActionStyle{}
+
+ if backgroundColor, ok := styleMap["backgroundColor"].(string); ok {
+ style.BackgroundColor = &backgroundColor
+ }
+
+ return style
+}
+
+// convert2DStringArrayPreserveEmpty is like convert2DStringArray but returns
+// an empty slice (not nil) when input is empty, to ensure JSON marshals as []
+func convert2DStringArrayPreserveEmpty(arr []interface{}) [][]string {
+ // Return empty slice (not nil) to preserve [] in JSON output
+ result := make([][]string, 0, len(arr))
+ for _, item := range arr {
+ if innerArr, ok := item.([]interface{}); ok {
+ stringArr := make([]string, 0, len(innerArr))
+ for _, s := range innerArr {
+ if str, ok := s.(string); ok {
+ stringArr = append(stringArr, str)
+ }
+ }
+ result = append(result, stringArr)
+ }
+ }
+
+ return result
+}
+
// getAngularPanelMigration is a convenience wrapper around schemaversion.GetAngularPanelMigration.
// It checks if a panel type is an Angular panel and returns the new type to migrate to.
// Returns the new panel type if migration is needed, empty string otherwise.
diff --git a/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v1beta1.go b/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v1beta1.go
index 5dc7ecf21fd..f9f953f965d 100644
--- a/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v1beta1.go
+++ b/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v1beta1.go
@@ -71,11 +71,6 @@ func convertDashboardSpec_V2alpha1_to_V1beta1(in *dashv2alpha1.DashboardSpec) (m
if err != nil {
return nil, fmt.Errorf("failed to convert panels: %w", err)
}
- // Count total panels including those in collapsed rows
- totalPanelsConverted := countTotalPanels(panels)
- if totalPanelsConverted < len(in.Elements) {
- return nil, fmt.Errorf("some panels were not converted from v2alpha1 to v1beta1")
- }
if len(panels) > 0 {
dashboard["panels"] = panels
@@ -198,29 +193,6 @@ func convertLinksToV1(links []dashv2alpha1.DashboardDashboardLink) []map[string]
return result
}
-// countTotalPanels counts all panels including those nested in collapsed row panels.
-func countTotalPanels(panels []interface{}) int {
- count := 0
- for _, p := range panels {
- panel, ok := p.(map[string]interface{})
- if !ok {
- count++
- continue
- }
-
- // Check if this is a row panel with nested panels
- if panelType, ok := panel["type"].(string); ok && panelType == "row" {
- if nestedPanels, ok := panel["panels"].([]interface{}); ok {
- count += len(nestedPanels)
- }
- // Don't count the row itself as a panel element
- } else {
- count++
- }
- }
- return count
-}
-
// convertPanelsFromElementsAndLayout converts V2 layout structures to V1 panel arrays.
// V1 only supports a flat array of panels with row panels for grouping.
// This function dispatches to the appropriate converter based on layout type:
@@ -467,6 +439,11 @@ func processTabItem(elements map[string]dashv2alpha1.DashboardElement, tab *dash
rowPanel["title"] = *tab.Spec.Title
}
+ if tab.Spec.Repeat != nil && tab.Spec.Repeat.Value != "" {
+ // We only use value here as V1 doesn't support mode
+ rowPanel["repeat"] = tab.Spec.Repeat.Value
+ }
+
rowPanel["gridPos"] = map[string]interface{}{
"x": 0,
"y": currentY,
@@ -847,6 +824,21 @@ func convertAutoGridLayoutToPanelsWithOffset(elements map[string]dashv2alpha1.Da
},
}
+ // Convert AutoGridRepeatOptions to RepeatOptions if present
+ // AutoGridRepeatOptions only has mode and value; infer direction and maxPerRow from AutoGrid settings:
+ // - direction: always "h" (AutoGrid flows horizontally, left-to-right then wraps)
+ // - maxPerRow: from AutoGrid's maxColumnCount
+ if item.Spec.Repeat != nil {
+ directionH := dashv2alpha1.DashboardRepeatOptionsDirectionH
+ maxPerRow := int64(maxColumnCount)
+ gridItem.Spec.Repeat = &dashv2alpha1.DashboardRepeatOptions{
+ Mode: item.Spec.Repeat.Mode,
+ Value: item.Spec.Repeat.Value,
+ Direction: &directionH,
+ MaxPerRow: &maxPerRow,
+ }
+ }
+
panel, err := convertPanelFromElement(&element, &gridItem)
if err != nil {
return nil, fmt.Errorf("failed to convert panel %s: %w", item.Spec.Element.Name, err)
@@ -1090,6 +1082,17 @@ func convertPanelKindToV1(panelKind *dashv2alpha1.DashboardPanelKind, panel map[
"id": t.Spec.Id,
"options": t.Spec.Options,
}
+ // Add disabled if set
+ if t.Spec.Disabled != nil {
+ transformation["disabled"] = *t.Spec.Disabled
+ }
+ // Add filter if set
+ if t.Spec.Filter != nil {
+ transformation["filter"] = map[string]interface{}{
+ "id": t.Spec.Filter.Id,
+ "options": t.Spec.Filter.Options,
+ }
+ }
transformations = append(transformations, transformation)
}
panel["transformations"] = transformations
@@ -1985,9 +1988,18 @@ func convertFieldConfigDefaultsToV1(defaults *dashv2alpha1.DashboardFieldConfig)
if defaults.Writeable != nil {
result["writeable"] = *defaults.Writeable
}
+ if defaults.FieldMinMax != nil {
+ result["fieldMinMax"] = *defaults.FieldMinMax
+ }
+ if defaults.NullValueMode != nil {
+ result["nullValueMode"] = string(*defaults.NullValueMode)
+ }
if defaults.Links != nil {
result["links"] = defaults.Links
}
+ if len(defaults.Actions) > 0 {
+ result["actions"] = convertActionsToV1(defaults.Actions)
+ }
if defaults.Color != nil {
result["color"] = convertFieldColorToV1(defaults.Color)
}
@@ -2193,3 +2205,115 @@ func convertThresholdsToV1(thresholds *dashv2alpha1.DashboardThresholdsConfig) m
return thresholdsMap
}
+
+func convertActionsToV1(actions []dashv2alpha1.DashboardAction) []map[string]interface{} {
+ result := make([]map[string]interface{}, 0, len(actions))
+
+ for _, action := range actions {
+ actionMap := map[string]interface{}{
+ "type": string(action.Type),
+ "title": action.Title,
+ }
+
+ if action.Confirmation != nil {
+ actionMap["confirmation"] = *action.Confirmation
+ }
+
+ if action.OneClick != nil {
+ actionMap["oneClick"] = *action.OneClick
+ }
+
+ if action.Fetch != nil {
+ actionMap["fetch"] = convertFetchOptionsToV1(action.Fetch)
+ }
+
+ if action.Infinity != nil {
+ actionMap["infinity"] = convertInfinityOptionsToV1(action.Infinity)
+ }
+
+ if len(action.Variables) > 0 {
+ actionMap["variables"] = convertActionVariablesToV1(action.Variables)
+ }
+
+ if action.Style != nil {
+ styleMap := map[string]interface{}{}
+ if action.Style.BackgroundColor != nil {
+ styleMap["backgroundColor"] = *action.Style.BackgroundColor
+ }
+ if len(styleMap) > 0 {
+ actionMap["style"] = styleMap
+ }
+ }
+
+ result = append(result, actionMap)
+ }
+
+ return result
+}
+
+func convertFetchOptionsToV1(fetch *dashv2alpha1.DashboardFetchOptions) map[string]interface{} {
+ result := map[string]interface{}{
+ "method": string(fetch.Method),
+ "url": fetch.Url,
+ }
+
+ if fetch.Body != nil {
+ result["body"] = *fetch.Body
+ }
+
+ if len(fetch.QueryParams) > 0 {
+ result["queryParams"] = convert2DStringArrayToInterface(fetch.QueryParams)
+ }
+
+ if len(fetch.Headers) > 0 {
+ result["headers"] = convert2DStringArrayToInterface(fetch.Headers)
+ }
+
+ return result
+}
+
+func convertInfinityOptionsToV1(infinity *dashv2alpha1.DashboardInfinityOptions) map[string]interface{} {
+ result := map[string]interface{}{
+ "method": string(infinity.Method),
+ "url": infinity.Url,
+ "datasourceUid": infinity.DatasourceUid,
+ }
+
+ if infinity.Body != nil {
+ result["body"] = *infinity.Body
+ }
+
+ if len(infinity.QueryParams) > 0 {
+ result["queryParams"] = convert2DStringArrayToInterface(infinity.QueryParams)
+ }
+
+ if len(infinity.Headers) > 0 {
+ result["headers"] = convert2DStringArrayToInterface(infinity.Headers)
+ }
+
+ return result
+}
+
+func convertActionVariablesToV1(variables []dashv2alpha1.DashboardActionVariable) []map[string]interface{} {
+ result := make([]map[string]interface{}, 0, len(variables))
+ for _, v := range variables {
+ result = append(result, map[string]interface{}{
+ "key": v.Key,
+ "name": v.Name,
+ "type": v.Type,
+ })
+ }
+ return result
+}
+
+func convert2DStringArrayToInterface(arr [][]string) []interface{} {
+ result := make([]interface{}, 0, len(arr))
+ for _, innerArr := range arr {
+ interfaceArr := make([]interface{}, 0, len(innerArr))
+ for _, s := range innerArr {
+ interfaceArr = append(interfaceArr, s)
+ }
+ result = append(result, interfaceArr)
+ }
+ return result
+}
diff --git a/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v2beta1.go b/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v2beta1.go
index 45803b6d7ec..8435c56d83f 100644
--- a/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v2beta1.go
+++ b/apps/dashboard/pkg/migration/conversion/v2alpha1_to_v2beta1.go
@@ -310,6 +310,9 @@ func convertFieldConfig_V2alpha1_to_V2beta1(in *dashv2alpha1.DashboardFieldConfi
Links: in.Links,
NoValue: in.NoValue,
Custom: in.Custom,
+ FieldMinMax: in.FieldMinMax,
+ NullValueMode: (*dashv2beta1.DashboardNullValueMode)(in.NullValueMode),
+ Actions: convertActions_V2alpha1_to_V2beta1(in.Actions),
}
// Convert thresholds
@@ -1021,3 +1024,59 @@ func convertAnnotationMappings_V2alpha1_to_V2beta1(in map[string]dashv2alpha1.Da
}
return out
}
+
+func convertActions_V2alpha1_to_V2beta1(in []dashv2alpha1.DashboardAction) []dashv2beta1.DashboardAction {
+ if len(in) == 0 {
+ return nil
+ }
+
+ out := make([]dashv2beta1.DashboardAction, len(in))
+ for i, action := range in {
+ out[i] = dashv2beta1.DashboardAction{
+ Type: dashv2beta1.DashboardActionType(action.Type),
+ Title: action.Title,
+ Confirmation: action.Confirmation,
+ OneClick: action.OneClick,
+ }
+
+ if action.Fetch != nil {
+ out[i].Fetch = &dashv2beta1.DashboardFetchOptions{
+ Method: dashv2beta1.DashboardHttpRequestMethod(action.Fetch.Method),
+ Url: action.Fetch.Url,
+ Body: action.Fetch.Body,
+ QueryParams: action.Fetch.QueryParams,
+ Headers: action.Fetch.Headers,
+ }
+ }
+
+ if action.Infinity != nil {
+ out[i].Infinity = &dashv2beta1.DashboardInfinityOptions{
+ Method: dashv2beta1.DashboardHttpRequestMethod(action.Infinity.Method),
+ Url: action.Infinity.Url,
+ Body: action.Infinity.Body,
+ QueryParams: action.Infinity.QueryParams,
+ Headers: action.Infinity.Headers,
+ DatasourceUid: action.Infinity.DatasourceUid,
+ }
+ }
+
+ if len(action.Variables) > 0 {
+ out[i].Variables = make([]dashv2beta1.DashboardActionVariable, len(action.Variables))
+ for j, v := range action.Variables {
+ out[i].Variables[j] = dashv2beta1.DashboardActionVariable{
+ Key: v.Key,
+ Name: v.Name,
+ Type: v.Type,
+ }
+ }
+ }
+
+ if action.Style != nil {
+ out[i].Style = &dashv2beta1.DashboardV2beta1ActionStyle{
+ BackgroundColor: action.Style.BackgroundColor,
+ }
+ }
+ }
+
+ return out
+}
diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-loki/loki_query_splitting.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-loki/loki_query_splitting.v42.json
index a7beffa4cdc..8af239195cb 100644
--- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-loki/loki_query_splitting.v42.json
+++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-loki/loki_query_splitting.v42.json
@@ -219,8 +219,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -312,8 +311,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -492,8 +490,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -584,8 +581,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -676,8 +672,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -791,8 +786,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -906,8 +900,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -1022,8 +1015,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests.v42.json
index 635103053bf..87b63411976 100644
--- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests.v42.json
+++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests.v42.json
@@ -65,17 +65,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -136,17 +133,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -207,17 +201,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -271,7 +262,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -279,17 +269,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -342,7 +329,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -350,17 +336,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -414,7 +397,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -422,17 +404,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -485,7 +464,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -493,17 +471,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -682,7 +657,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"options": {
@@ -699,17 +673,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -764,7 +735,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"options": {
@@ -782,17 +752,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -847,7 +814,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"options": {
@@ -866,17 +832,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -931,7 +894,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"options": {
@@ -960,17 +922,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -1052,7 +1011,7 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "2",
+ "decimals": 2,
"mappings": [],
"max": 100,
"min": 0,
@@ -1060,17 +1019,14 @@
"mode": "absolute",
"steps": [
{
- "color": "#7EB26D",
- "index": 0
+ "color": "#7EB26D"
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json
index 61f092d491e..7fdd474df87 100644
--- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json
+++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json
@@ -2117,7 +2117,7 @@
}
],
"title": "Numeric, no series",
- "type": "gauge"
+ "type": "radialbar"
},
{
"datasource": {
@@ -2183,7 +2183,7 @@
}
],
"title": "Non-numeric",
- "type": "gauge"
+ "type": "radialbar"
}
],
"preload": false,
@@ -2201,4 +2201,4 @@
"title": "Panel tests - Gauge (new)",
"uid": "panel-tests-gauge-new",
"weekStart": ""
-}
\ No newline at end of file
+}
diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-histogram/histogram_tests.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-histogram/histogram_tests.v42.json
index 7e392bd55d0..6c521eaec9b 100644
--- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-histogram/histogram_tests.v42.json
+++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-histogram/histogram_tests.v42.json
@@ -58,8 +58,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -127,8 +126,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -196,8 +194,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -277,8 +274,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -355,8 +351,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -448,8 +443,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -536,8 +530,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -619,8 +612,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -702,8 +694,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -785,8 +776,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -850,8 +840,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-piechart/panel_test_piechart.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-piechart/panel_test_piechart.v42.json
index 5b07f246ae3..f705124be5b 100644
--- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-piechart/panel_test_piechart.v42.json
+++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-piechart/panel_test_piechart.v42.json
@@ -290,7 +290,7 @@
],
"legend": {
"displayMode": "table",
- "placement": "right",
+ "placement": "bottom",
"showLegend": true,
"values": [
"percent"
@@ -304,7 +304,7 @@
"fields": "",
"values": false
},
- "showLegend": true,
+ "showLegend": false,
"strokeWidth": 1,
"text": {}
},
@@ -323,15 +323,6 @@
}
],
"title": "Percent",
- "transformations": [
- {
- "id": "renameByRegex",
- "options": {
- "regex": "^Backend-(.*)$",
- "renamePattern": "b-$1"
- }
- }
- ],
"type": "piechart"
},
{
@@ -375,7 +366,7 @@
],
"legend": {
"displayMode": "table",
- "placement": "right",
+ "placement": "bottom",
"showLegend": true,
"values": [
"value"
@@ -389,7 +380,7 @@
"fields": "",
"values": false
},
- "showLegend": true,
+ "showLegend": false,
"strokeWidth": 1,
"text": {}
},
@@ -408,15 +399,6 @@
}
],
"title": "Value",
- "transformations": [
- {
- "id": "renameByRegex",
- "options": {
- "regex": "(.*)",
- "renamePattern": "$1-how-much-wood-could-a-woodchuck-chuck-if-a-woodchuck-could-chuck-wood"
- }
- }
- ],
"type": "piechart"
},
{
diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-xychart/xychart-tooltip-color-test.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-xychart/xychart-tooltip-color-test.v42.json
index 417ea1661e1..f28fee864e5 100644
--- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-xychart/xychart-tooltip-color-test.v42.json
+++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-xychart/xychart-tooltip-color-test.v42.json
@@ -61,8 +61,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -148,8 +147,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -235,8 +233,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -322,8 +319,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -416,8 +412,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -510,8 +505,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -604,8 +598,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
diff --git a/apps/plugins/Makefile b/apps/plugins/Makefile
index 230bfd4149a..2db266ef19b 100644
--- a/apps/plugins/Makefile
+++ b/apps/plugins/Makefile
@@ -1,9 +1,16 @@
include ../sdk.mk
-.PHONY: generate # Run Grafana App SDK code generation
-generate: install-app-sdk update-app-sdk
+.PHONY: internal-generate # Run Grafana App SDK code generation
+internal-generate: install-app-sdk update-app-sdk
@$(APP_SDK_BIN) generate \
--source=./kinds/ \
--gogenpath=./pkg/apis \
--grouping=group \
- --defencoding=none
\ No newline at end of file
+ --defencoding=none
+
+.PHONY: generate
+generate: internal-generate # copy files to packages/grafana-runtime/src/services/pluginMeta/types
+ rm -f ./packages/grafana-runtime/src/services/pluginMeta/types/*.ts
+ cp plugin/src/generated/meta/v0alpha1/meta_object_gen.ts ../../packages/grafana-runtime/src/services/pluginMeta/types/meta_object_gen.ts
+ cp plugin/src/generated/meta/v0alpha1/types.spec.gen.ts ../../packages/grafana-runtime/src/services/pluginMeta/types/types.spec.gen.ts
+ cp plugin/src/generated/meta/v0alpha1/types.status.gen.ts ../../packages/grafana-runtime/src/services/pluginMeta/types/types.status.gen.ts
\ No newline at end of file
diff --git a/apps/plugins/README.md b/apps/plugins/README.md
index 7f91dd6ea12..f21fa6701b5 100644
--- a/apps/plugins/README.md
+++ b/apps/plugins/README.md
@@ -4,8 +4,7 @@ API documentation is available at http://localhost:3000/swagger?api=plugins.graf
## Codegen
-- Go: `make generate`
-- Frontend: Follow instructions in this [README](../..//packages/grafana-api-clients/README.md)
+- Go and TypeScript: `make generate`
## Plugin sync
diff --git a/apps/plugins/go.mod b/apps/plugins/go.mod
index e4866731967..7e9e5f47876 100644
--- a/apps/plugins/go.mod
+++ b/apps/plugins/go.mod
@@ -97,7 +97,7 @@ require (
github.com/google/gnostic-models v0.7.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/grafana/alerting v0.0.0-20251231150637-b7821017d69f // indirect
+ github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f // indirect
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f // indirect
github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 // indirect
github.com/grafana/dataplane/sdata v0.0.9 // indirect
diff --git a/apps/plugins/go.sum b/apps/plugins/go.sum
index 29738a48cca..c991111f90e 100644
--- a/apps/plugins/go.sum
+++ b/apps/plugins/go.sum
@@ -215,8 +215,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
-github.com/grafana/alerting v0.0.0-20251231150637-b7821017d69f h1:Br4SaUL3dnVopKKNhDavCLgehw60jdtl/sIxdfzmVts=
-github.com/grafana/alerting v0.0.0-20251231150637-b7821017d69f/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU=
+github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f h1:3bXOyht68qkfvD6Y8z8XoenFbytSSOIkr/s+AqRzj0o=
+github.com/grafana/alerting v0.0.0-20260112172717-98a49ed9557f/go.mod h1:Ji0SfJChcwjgq8ljy6Y5CcYfHfAYKXjKYeysOoDS/6s=
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f h1:Cbm6OKkOcJ+7CSZsGsEJzktC/SIa5bxVeYKQLuYK86o=
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f/go.mod h1:axY0cdOg3q0TZHwpHnIz5x16xZ8ZBxJHShsSHHXcHQg=
github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 h1:Muoy+FMGrHj3GdFbvsMzUT7eusgii9PKf9L1ZaXDDbY=
diff --git a/apps/plugins/kinds/manifest.cue b/apps/plugins/kinds/manifest.cue
index f624dc117bc..680a0f7565d 100644
--- a/apps/plugins/kinds/manifest.cue
+++ b/apps/plugins/kinds/manifest.cue
@@ -11,7 +11,7 @@ manifest: {
v0alpha1Version: {
served: true
codegen: {
- ts: {enabled: false}
+ ts: {enabled: true}
go: {enabled: true}
}
kinds: [
diff --git a/apps/plugins/kinds/meta.cue b/apps/plugins/kinds/meta.cue
index 01dc45adf77..479a9111d24 100644
--- a/apps/plugins/kinds/meta.cue
+++ b/apps/plugins/kinds/meta.cue
@@ -18,9 +18,6 @@ metaV0Alpha1: {
type?: "grafana" | "commercial" | "community" | "private" | "private-glob"
org?: string
}
- angular?: {
- detected: bool
- }
translations?: [string]: string
// +listType=atomic
children?: [...string]
diff --git a/apps/plugins/pkg/apis/plugins/v0alpha1/meta_spec_gen.go b/apps/plugins/pkg/apis/plugins/v0alpha1/meta_spec_gen.go
index 631febbe2fa..141e9e5ad82 100644
--- a/apps/plugins/pkg/apis/plugins/v0alpha1/meta_spec_gen.go
+++ b/apps/plugins/pkg/apis/plugins/v0alpha1/meta_spec_gen.go
@@ -215,7 +215,6 @@ type MetaSpec struct {
Module *MetaV0alpha1SpecModule `json:"module,omitempty"`
BaseURL *string `json:"baseURL,omitempty"`
Signature *MetaV0alpha1SpecSignature `json:"signature,omitempty"`
- Angular *MetaV0alpha1SpecAngular `json:"angular,omitempty"`
Translations map[string]string `json:"translations,omitempty"`
// +listType=atomic
Children []string `json:"children,omitempty"`
@@ -461,16 +460,6 @@ func NewMetaV0alpha1SpecSignature() *MetaV0alpha1SpecSignature {
return &MetaV0alpha1SpecSignature{}
}
-// +k8s:openapi-gen=true
-type MetaV0alpha1SpecAngular struct {
- Detected bool `json:"detected"`
-}
-
-// NewMetaV0alpha1SpecAngular creates a new MetaV0alpha1SpecAngular object.
-func NewMetaV0alpha1SpecAngular() *MetaV0alpha1SpecAngular {
- return &MetaV0alpha1SpecAngular{}
-}
-
// +k8s:openapi-gen=true
type MetaJSONDataType string
diff --git a/apps/plugins/pkg/apis/plugins_manifest.go b/apps/plugins/pkg/apis/plugins_manifest.go
index 0c52665e75d..f37c14ed0cf 100644
--- a/apps/plugins/pkg/apis/plugins_manifest.go
+++ b/apps/plugins/pkg/apis/plugins_manifest.go
@@ -23,7 +23,7 @@ var (
rawSchemaPluginv0alpha1 = []byte(`{"OperatorState":{"additionalProperties":false,"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"details contains any extra information that is operator-specific","type":"object"},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"Plugin":{"properties":{"spec":{"$ref":"#/components/schemas/spec"},"status":{"$ref":"#/components/schemas/status"}},"required":["spec"]},"spec":{"additionalProperties":false,"properties":{"id":{"type":"string"},"url":{"type":"string"},"version":{"type":"string"}},"required":["id","version"],"type":"object"},"status":{"additionalProperties":false,"properties":{"additionalFields":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"additionalFields is reserved for future use","type":"object"},"operatorStates":{"additionalProperties":{"$ref":"#/components/schemas/OperatorState"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object"}}`)
versionSchemaPluginv0alpha1 app.VersionSchema
_ = json.Unmarshal(rawSchemaPluginv0alpha1, &versionSchemaPluginv0alpha1)
- rawSchemaMetav0alpha1 = []byte(`{"Dependencies":{"additionalProperties":false,"properties":{"extensions":{"additionalProperties":false,"properties":{"exposedComponents":{"description":"+listType=set","items":{"type":"string"},"type":"array"}},"type":"object"},"grafanaDependency":{"description":"Required field","type":"string"},"grafanaVersion":{"description":"Optional fields","type":"string"},"plugins":{"description":"+listType=set\n+listMapKey=id","items":{"additionalProperties":false,"properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"enum":["app","datasource","panel"],"type":"string"}},"required":["id","type","name"],"type":"object"},"type":"array"}},"required":["grafanaDependency"],"type":"object"},"EnterpriseFeatures":{"additionalProperties":false,"properties":{"healthDiagnosticsErrors":{"default":false,"description":"Allow additional properties","type":"boolean"}},"type":"object"},"Extensions":{"additionalProperties":false,"properties":{"addedComponents":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"description":{"type":"string"},"targets":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"title":{"type":"string"}},"required":["targets","title"],"type":"object"},"type":"array"},"addedFunctions":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"description":{"type":"string"},"targets":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"title":{"type":"string"}},"required":["targets","title"],"type":"object"},"type":"array"},"addedLinks":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"description":{"type":"string"},"targets":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"title":{"type":"string"}},"required":["targets","title"],"type":"object"},"type":"array"},"exposedComponents":{"description":"+listType=set\n+listMapKey=id","items":{"additionalProperties":false,"properties":{"description":{"type":"string"},"id":{"type":"string"},"title":{"type":"string"}},"required":["id"],"type":"object"},"type":"array"},"extensionPoints":{"description":"+listType=set\n+listMapKey=id","items":{"additionalProperties":false,"properties":{"description":{"type":"string"},"id":{"type":"string"},"title":{"type":"string"}},"required":["id"],"type":"object"},"type":"array"}},"type":"object"},"IAM":{"additionalProperties":false,"properties":{"permissions":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"action":{"type":"string"},"scope":{"type":"string"}},"type":"object"},"type":"array"}},"type":"object"},"Include":{"additionalProperties":false,"properties":{"action":{"type":"string"},"addToNav":{"type":"boolean"},"component":{"type":"string"},"defaultNav":{"type":"boolean"},"icon":{"type":"string"},"name":{"type":"string"},"path":{"type":"string"},"role":{"enum":["Admin","Editor","Viewer","None"],"type":"string"},"type":{"enum":["dashboard","page","panel","datasource"],"type":"string"},"uid":{"type":"string"}},"type":"object"},"Info":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"description":"Optional fields","properties":{"email":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}},"type":"object"},"description":{"type":"string"},"keywords":{"description":"Required fields\n+listType=set","items":{"type":"string"},"type":"array"},"links":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"name":{"type":"string"},"url":{"type":"string"}},"type":"object"},"type":"array"},"logos":{"additionalProperties":false,"properties":{"large":{"type":"string"},"small":{"type":"string"}},"required":["small","large"],"type":"object"},"screenshots":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"name":{"type":"string"},"path":{"type":"string"}},"type":"object"},"type":"array"},"updated":{"type":"string"},"version":{"type":"string"}},"required":["keywords","logos","updated","version"],"type":"object"},"JSONData":{"additionalProperties":false,"description":"JSON configuration schema for Grafana plugins\nConverted from: https://github.com/grafana/grafana/blob/main/docs/sources/developers/plugins/plugin.schema.json","properties":{"alerting":{"description":"Optional fields","type":"boolean"},"annotations":{"type":"boolean"},"autoEnabled":{"type":"boolean"},"backend":{"type":"boolean"},"buildMode":{"type":"string"},"builtIn":{"type":"boolean"},"category":{"enum":["tsdb","logging","cloud","tracing","profiling","sql","enterprise","iot","other"],"type":"string"},"dependencies":{"$ref":"#/components/schemas/Dependencies","description":"Dependency information"},"enterpriseFeatures":{"$ref":"#/components/schemas/EnterpriseFeatures"},"executable":{"type":"string"},"extensions":{"$ref":"#/components/schemas/Extensions"},"hideFromList":{"type":"boolean"},"iam":{"$ref":"#/components/schemas/IAM"},"id":{"description":"Unique name of the plugin","type":"string"},"includes":{"description":"+listType=atomic","items":{"$ref":"#/components/schemas/Include"},"type":"array"},"info":{"$ref":"#/components/schemas/Info","description":"Metadata for the plugin"},"logs":{"type":"boolean"},"metrics":{"type":"boolean"},"multiValueFilterOperators":{"type":"boolean"},"name":{"description":"Human-readable name of the plugin","type":"string"},"pascalName":{"type":"string"},"preload":{"type":"boolean"},"queryOptions":{"$ref":"#/components/schemas/QueryOptions"},"roles":{"description":"+listType=atomic","items":{"$ref":"#/components/schemas/Role"},"type":"array"},"routes":{"description":"+listType=atomic","items":{"$ref":"#/components/schemas/Route"},"type":"array"},"skipDataQuery":{"type":"boolean"},"state":{"enum":["alpha","beta"],"type":"string"},"streaming":{"type":"boolean"},"suggestions":{"type":"boolean"},"tracing":{"type":"boolean"},"type":{"description":"Plugin type","enum":["app","datasource","panel","renderer"],"type":"string"}},"required":["id","type","name","info","dependencies"],"type":"object"},"Meta":{"properties":{"spec":{"$ref":"#/components/schemas/spec"},"status":{"$ref":"#/components/schemas/status"}},"required":["spec"]},"OperatorState":{"additionalProperties":false,"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"details contains any extra information that is operator-specific","type":"object"},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"QueryOptions":{"additionalProperties":false,"properties":{"cacheTimeout":{"type":"boolean"},"maxDataPoints":{"type":"boolean"},"minInterval":{"type":"boolean"}},"type":"object"},"Role":{"additionalProperties":false,"properties":{"grants":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"role":{"additionalProperties":false,"properties":{"description":{"type":"string"},"name":{"type":"string"},"permissions":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"action":{"type":"string"},"scope":{"type":"string"}},"type":"object"},"type":"array"}},"type":"object"}},"type":"object"},"Route":{"additionalProperties":false,"properties":{"body":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"},"headers":{"description":"+listType=atomic","items":{"type":"string"},"type":"array"},"jwtTokenAuth":{"additionalProperties":false,"properties":{"params":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"},"scopes":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"url":{"type":"string"}},"type":"object"},"method":{"type":"string"},"path":{"type":"string"},"reqAction":{"type":"string"},"reqRole":{"type":"string"},"reqSignedIn":{"type":"boolean"},"tokenAuth":{"additionalProperties":false,"properties":{"params":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"},"scopes":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"url":{"type":"string"}},"type":"object"},"url":{"type":"string"},"urlParams":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"content":{"type":"string"},"name":{"type":"string"}},"type":"object"},"type":"array"}},"type":"object"},"spec":{"additionalProperties":false,"properties":{"angular":{"additionalProperties":false,"properties":{"detected":{"type":"boolean"}},"required":["detected"],"type":"object"},"baseURL":{"type":"string"},"children":{"description":"+listType=atomic","items":{"type":"string"},"type":"array"},"class":{"enum":["core","external"],"type":"string"},"module":{"additionalProperties":false,"properties":{"hash":{"type":"string"},"loadingStrategy":{"enum":["fetch","script"],"type":"string"},"path":{"type":"string"}},"required":["path"],"type":"object"},"pluginJson":{"$ref":"#/components/schemas/JSONData"},"signature":{"additionalProperties":false,"properties":{"org":{"type":"string"},"status":{"enum":["internal","valid","invalid","modified","unsigned"],"type":"string"},"type":{"enum":["grafana","commercial","community","private","private-glob"],"type":"string"}},"required":["status"],"type":"object"},"translations":{"additionalProperties":{"type":"string"},"type":"object"}},"required":["pluginJson","class"],"type":"object"},"status":{"additionalProperties":false,"properties":{"additionalFields":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"additionalFields is reserved for future use","type":"object"},"operatorStates":{"additionalProperties":{"$ref":"#/components/schemas/OperatorState"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object"}}`)
+ rawSchemaMetav0alpha1 = []byte(`{"Dependencies":{"additionalProperties":false,"properties":{"extensions":{"additionalProperties":false,"properties":{"exposedComponents":{"description":"+listType=set","items":{"type":"string"},"type":"array"}},"type":"object"},"grafanaDependency":{"description":"Required field","type":"string"},"grafanaVersion":{"description":"Optional fields","type":"string"},"plugins":{"description":"+listType=set\n+listMapKey=id","items":{"additionalProperties":false,"properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"enum":["app","datasource","panel"],"type":"string"}},"required":["id","type","name"],"type":"object"},"type":"array"}},"required":["grafanaDependency"],"type":"object"},"EnterpriseFeatures":{"additionalProperties":false,"properties":{"healthDiagnosticsErrors":{"default":false,"description":"Allow additional properties","type":"boolean"}},"type":"object"},"Extensions":{"additionalProperties":false,"properties":{"addedComponents":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"description":{"type":"string"},"targets":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"title":{"type":"string"}},"required":["targets","title"],"type":"object"},"type":"array"},"addedFunctions":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"description":{"type":"string"},"targets":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"title":{"type":"string"}},"required":["targets","title"],"type":"object"},"type":"array"},"addedLinks":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"description":{"type":"string"},"targets":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"title":{"type":"string"}},"required":["targets","title"],"type":"object"},"type":"array"},"exposedComponents":{"description":"+listType=set\n+listMapKey=id","items":{"additionalProperties":false,"properties":{"description":{"type":"string"},"id":{"type":"string"},"title":{"type":"string"}},"required":["id"],"type":"object"},"type":"array"},"extensionPoints":{"description":"+listType=set\n+listMapKey=id","items":{"additionalProperties":false,"properties":{"description":{"type":"string"},"id":{"type":"string"},"title":{"type":"string"}},"required":["id"],"type":"object"},"type":"array"}},"type":"object"},"IAM":{"additionalProperties":false,"properties":{"permissions":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"action":{"type":"string"},"scope":{"type":"string"}},"type":"object"},"type":"array"}},"type":"object"},"Include":{"additionalProperties":false,"properties":{"action":{"type":"string"},"addToNav":{"type":"boolean"},"component":{"type":"string"},"defaultNav":{"type":"boolean"},"icon":{"type":"string"},"name":{"type":"string"},"path":{"type":"string"},"role":{"enum":["Admin","Editor","Viewer","None"],"type":"string"},"type":{"enum":["dashboard","page","panel","datasource"],"type":"string"},"uid":{"type":"string"}},"type":"object"},"Info":{"additionalProperties":false,"properties":{"author":{"additionalProperties":false,"description":"Optional fields","properties":{"email":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}},"type":"object"},"description":{"type":"string"},"keywords":{"description":"Required fields\n+listType=set","items":{"type":"string"},"type":"array"},"links":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"name":{"type":"string"},"url":{"type":"string"}},"type":"object"},"type":"array"},"logos":{"additionalProperties":false,"properties":{"large":{"type":"string"},"small":{"type":"string"}},"required":["small","large"],"type":"object"},"screenshots":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"name":{"type":"string"},"path":{"type":"string"}},"type":"object"},"type":"array"},"updated":{"type":"string"},"version":{"type":"string"}},"required":["keywords","logos","updated","version"],"type":"object"},"JSONData":{"additionalProperties":false,"description":"JSON configuration schema for Grafana plugins\nConverted from: https://github.com/grafana/grafana/blob/main/docs/sources/developers/plugins/plugin.schema.json","properties":{"alerting":{"description":"Optional fields","type":"boolean"},"annotations":{"type":"boolean"},"autoEnabled":{"type":"boolean"},"backend":{"type":"boolean"},"buildMode":{"type":"string"},"builtIn":{"type":"boolean"},"category":{"enum":["tsdb","logging","cloud","tracing","profiling","sql","enterprise","iot","other"],"type":"string"},"dependencies":{"$ref":"#/components/schemas/Dependencies","description":"Dependency information"},"enterpriseFeatures":{"$ref":"#/components/schemas/EnterpriseFeatures"},"executable":{"type":"string"},"extensions":{"$ref":"#/components/schemas/Extensions"},"hideFromList":{"type":"boolean"},"iam":{"$ref":"#/components/schemas/IAM"},"id":{"description":"Unique name of the plugin","type":"string"},"includes":{"description":"+listType=atomic","items":{"$ref":"#/components/schemas/Include"},"type":"array"},"info":{"$ref":"#/components/schemas/Info","description":"Metadata for the plugin"},"logs":{"type":"boolean"},"metrics":{"type":"boolean"},"multiValueFilterOperators":{"type":"boolean"},"name":{"description":"Human-readable name of the plugin","type":"string"},"pascalName":{"type":"string"},"preload":{"type":"boolean"},"queryOptions":{"$ref":"#/components/schemas/QueryOptions"},"roles":{"description":"+listType=atomic","items":{"$ref":"#/components/schemas/Role"},"type":"array"},"routes":{"description":"+listType=atomic","items":{"$ref":"#/components/schemas/Route"},"type":"array"},"skipDataQuery":{"type":"boolean"},"state":{"enum":["alpha","beta"],"type":"string"},"streaming":{"type":"boolean"},"suggestions":{"type":"boolean"},"tracing":{"type":"boolean"},"type":{"description":"Plugin type","enum":["app","datasource","panel","renderer"],"type":"string"}},"required":["id","type","name","info","dependencies"],"type":"object"},"Meta":{"properties":{"spec":{"$ref":"#/components/schemas/spec"},"status":{"$ref":"#/components/schemas/status"}},"required":["spec"]},"OperatorState":{"additionalProperties":false,"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"details contains any extra information that is operator-specific","type":"object"},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"QueryOptions":{"additionalProperties":false,"properties":{"cacheTimeout":{"type":"boolean"},"maxDataPoints":{"type":"boolean"},"minInterval":{"type":"boolean"}},"type":"object"},"Role":{"additionalProperties":false,"properties":{"grants":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"role":{"additionalProperties":false,"properties":{"description":{"type":"string"},"name":{"type":"string"},"permissions":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"action":{"type":"string"},"scope":{"type":"string"}},"type":"object"},"type":"array"}},"type":"object"}},"type":"object"},"Route":{"additionalProperties":false,"properties":{"body":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"},"headers":{"description":"+listType=atomic","items":{"type":"string"},"type":"array"},"jwtTokenAuth":{"additionalProperties":false,"properties":{"params":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"},"scopes":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"url":{"type":"string"}},"type":"object"},"method":{"type":"string"},"path":{"type":"string"},"reqAction":{"type":"string"},"reqRole":{"type":"string"},"reqSignedIn":{"type":"boolean"},"tokenAuth":{"additionalProperties":false,"properties":{"params":{"additionalProperties":{"additionalProperties":{},"type":"object"},"type":"object"},"scopes":{"description":"+listType=set","items":{"type":"string"},"type":"array"},"url":{"type":"string"}},"type":"object"},"url":{"type":"string"},"urlParams":{"description":"+listType=atomic","items":{"additionalProperties":false,"properties":{"content":{"type":"string"},"name":{"type":"string"}},"type":"object"},"type":"array"}},"type":"object"},"spec":{"additionalProperties":false,"properties":{"baseURL":{"type":"string"},"children":{"description":"+listType=atomic","items":{"type":"string"},"type":"array"},"class":{"enum":["core","external"],"type":"string"},"module":{"additionalProperties":false,"properties":{"hash":{"type":"string"},"loadingStrategy":{"enum":["fetch","script"],"type":"string"},"path":{"type":"string"}},"required":["path"],"type":"object"},"pluginJson":{"$ref":"#/components/schemas/JSONData"},"signature":{"additionalProperties":false,"properties":{"org":{"type":"string"},"status":{"enum":["internal","valid","invalid","modified","unsigned"],"type":"string"},"type":{"enum":["grafana","commercial","community","private","private-glob"],"type":"string"}},"required":["status"],"type":"object"},"translations":{"additionalProperties":{"type":"string"},"type":"object"}},"required":["pluginJson","class"],"type":"object"},"status":{"additionalProperties":false,"properties":{"additionalFields":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"additionalFields is reserved for future use","type":"object"},"operatorStates":{"additionalProperties":{"$ref":"#/components/schemas/OperatorState"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object"}}`)
versionSchemaMetav0alpha1 app.VersionSchema
_ = json.Unmarshal(rawSchemaMetav0alpha1, &versionSchemaMetav0alpha1)
)
diff --git a/apps/plugins/pkg/app/meta/converter.go b/apps/plugins/pkg/app/meta/converter.go
index b8c0c4371d7..70a1bc78b0c 100644
--- a/apps/plugins/pkg/app/meta/converter.go
+++ b/apps/plugins/pkg/app/meta/converter.go
@@ -565,10 +565,6 @@ func pluginStorePluginToMeta(plugin pluginstore.Plugin, loadingStrategy plugins.
metaSpec.Children = plugin.Children
}
- metaSpec.Angular = &pluginsv0alpha1.MetaV0alpha1SpecAngular{
- Detected: plugin.Angular.Detected,
- }
-
if len(plugin.Translations) > 0 {
metaSpec.Translations = plugin.Translations
}
@@ -668,10 +664,6 @@ func pluginToMetaSpec(plugin *plugins.Plugin) pluginsv0alpha1.MetaSpec {
metaSpec.Children = children
}
- metaSpec.Angular = &pluginsv0alpha1.MetaV0alpha1SpecAngular{
- Detected: plugin.Angular.Detected,
- }
-
if len(plugin.Translations) > 0 {
metaSpec.Translations = plugin.Translations
}
@@ -712,8 +704,7 @@ type grafanaComPluginVersionMeta struct {
Rel string `json:"rel"`
Href string `json:"href"`
} `json:"links"`
- AngularDetected bool `json:"angularDetected"`
- Scopes []string `json:"scopes"`
+ Scopes []string `json:"scopes"`
}
// grafanaComPluginVersionMetaToMetaSpec converts a grafanaComPluginVersionMeta to a pluginsv0alpha1.MetaSpec.
@@ -753,10 +744,5 @@ func grafanaComPluginVersionMetaToMetaSpec(gcomMeta grafanaComPluginVersionMeta)
metaSpec.Signature = signature
}
- // Set angular info
- metaSpec.Angular = &pluginsv0alpha1.MetaV0alpha1SpecAngular{
- Detected: gcomMeta.AngularDetected,
- }
-
return metaSpec
}
diff --git a/apps/plugins/plugin/src/generated/meta/v0alpha1/meta_object_gen.ts b/apps/plugins/plugin/src/generated/meta/v0alpha1/meta_object_gen.ts
new file mode 100644
index 00000000000..044ec1f4cd8
--- /dev/null
+++ b/apps/plugins/plugin/src/generated/meta/v0alpha1/meta_object_gen.ts
@@ -0,0 +1,49 @@
+/*
+ * This file was generated by grafana-app-sdk. DO NOT EDIT.
+ */
+import { Spec } from './types.spec.gen';
+import { Status } from './types.status.gen';
+
+export interface Metadata {
+ name: string;
+ namespace: string;
+ generateName?: string;
+ selfLink?: string;
+ uid?: string;
+ resourceVersion?: string;
+ generation?: number;
+ creationTimestamp?: string;
+ deletionTimestamp?: string;
+ deletionGracePeriodSeconds?: number;
+ labels?: Record;
+ annotations?: Record;
+ ownerReferences?: OwnerReference[];
+ finalizers?: string[];
+ managedFields?: ManagedFieldsEntry[];
+}
+
+export interface OwnerReference {
+ apiVersion: string;
+ kind: string;
+ name: string;
+ uid: string;
+ controller?: boolean;
+ blockOwnerDeletion?: boolean;
+}
+
+export interface ManagedFieldsEntry {
+ manager?: string;
+ operation?: string;
+ apiVersion?: string;
+ time?: string;
+ fieldsType?: string;
+ subresource?: string;
+}
+
+export interface Meta {
+ kind: string;
+ apiVersion: string;
+ metadata: Metadata;
+ spec: Spec;
+ status: Status;
+}
diff --git a/apps/plugins/plugin/src/generated/meta/v0alpha1/types.metadata.gen.ts b/apps/plugins/plugin/src/generated/meta/v0alpha1/types.metadata.gen.ts
new file mode 100644
index 00000000000..4377f3c1d08
--- /dev/null
+++ b/apps/plugins/plugin/src/generated/meta/v0alpha1/types.metadata.gen.ts
@@ -0,0 +1,30 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+// metadata contains embedded CommonMetadata and can be extended with custom string fields
+// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
+// without external reference as using the CommonMetadata reference breaks thema codegen.
+export interface Metadata {
+ updateTimestamp: string;
+ createdBy: string;
+ uid: string;
+ creationTimestamp: string;
+ deletionTimestamp?: string;
+ finalizers: string[];
+ resourceVersion: string;
+ generation: number;
+ updatedBy: string;
+ labels: Record;
+}
+
+export const defaultMetadata = (): Metadata => ({
+ updateTimestamp: "",
+ createdBy: "",
+ uid: "",
+ creationTimestamp: "",
+ finalizers: [],
+ resourceVersion: "",
+ generation: 0,
+ updatedBy: "",
+ labels: {},
+});
+
diff --git a/apps/plugins/plugin/src/generated/meta/v0alpha1/types.spec.gen.ts b/apps/plugins/plugin/src/generated/meta/v0alpha1/types.spec.gen.ts
new file mode 100644
index 00000000000..51845e98454
--- /dev/null
+++ b/apps/plugins/plugin/src/generated/meta/v0alpha1/types.spec.gen.ts
@@ -0,0 +1,278 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+// JSON configuration schema for Grafana plugins
+// Converted from: https://github.com/grafana/grafana/blob/main/docs/sources/developers/plugins/plugin.schema.json
+export interface JSONData {
+ // Unique name of the plugin
+ id: string;
+ // Plugin type
+ type: "app" | "datasource" | "panel" | "renderer";
+ // Human-readable name of the plugin
+ name: string;
+ // Metadata for the plugin
+ info: Info;
+ // Dependency information
+ dependencies: Dependencies;
+ // Optional fields
+ alerting?: boolean;
+ annotations?: boolean;
+ autoEnabled?: boolean;
+ backend?: boolean;
+ buildMode?: string;
+ builtIn?: boolean;
+ category?: "tsdb" | "logging" | "cloud" | "tracing" | "profiling" | "sql" | "enterprise" | "iot" | "other";
+ enterpriseFeatures?: EnterpriseFeatures;
+ executable?: string;
+ hideFromList?: boolean;
+ // +listType=atomic
+ includes?: Include[];
+ logs?: boolean;
+ metrics?: boolean;
+ multiValueFilterOperators?: boolean;
+ pascalName?: string;
+ preload?: boolean;
+ queryOptions?: QueryOptions;
+ // +listType=atomic
+ routes?: Route[];
+ skipDataQuery?: boolean;
+ state?: "alpha" | "beta";
+ streaming?: boolean;
+ suggestions?: boolean;
+ tracing?: boolean;
+ iam?: IAM;
+ // +listType=atomic
+ roles?: Role[];
+ extensions?: Extensions;
+}
+
+export const defaultJSONData = (): JSONData => ({
+ id: "",
+ type: "app",
+ name: "",
+ info: defaultInfo(),
+ dependencies: defaultDependencies(),
+});
+
+export interface Info {
+ // Required fields
+ // +listType=set
+ keywords: string[];
+ logos: {
+ small: string;
+ large: string;
+ };
+ updated: string;
+ version: string;
+ // Optional fields
+ author?: {
+ name?: string;
+ email?: string;
+ url?: string;
+ };
+ description?: string;
+ // +listType=atomic
+ links?: {
+ name?: string;
+ url?: string;
+ }[];
+ // +listType=atomic
+ screenshots?: {
+ name?: string;
+ path?: string;
+ }[];
+}
+
+export const defaultInfo = (): Info => ({
+ keywords: [],
+ logos: {
+ small: "",
+ large: "",
+},
+ updated: "",
+ version: "",
+});
+
+export interface Dependencies {
+ // Required field
+ grafanaDependency: string;
+ // Optional fields
+ grafanaVersion?: string;
+ // +listType=set
+ // +listMapKey=id
+ plugins?: {
+ id: string;
+ type: "app" | "datasource" | "panel";
+ name: string;
+ }[];
+ extensions?: {
+ // +listType=set
+ exposedComponents?: string[];
+ };
+}
+
+export const defaultDependencies = (): Dependencies => ({
+ grafanaDependency: "",
+});
+
+export interface EnterpriseFeatures {
+ // Allow additional properties
+ healthDiagnosticsErrors?: boolean;
+}
+
+export const defaultEnterpriseFeatures = (): EnterpriseFeatures => ({
+ healthDiagnosticsErrors: false,
+});
+
+export interface Include {
+ uid?: string;
+ type?: "dashboard" | "page" | "panel" | "datasource";
+ name?: string;
+ component?: string;
+ role?: "Admin" | "Editor" | "Viewer" | "None";
+ action?: string;
+ path?: string;
+ addToNav?: boolean;
+ defaultNav?: boolean;
+ icon?: string;
+}
+
+export const defaultInclude = (): Include => ({
+});
+
+export interface QueryOptions {
+ maxDataPoints?: boolean;
+ minInterval?: boolean;
+ cacheTimeout?: boolean;
+}
+
+export const defaultQueryOptions = (): QueryOptions => ({
+});
+
+export interface Route {
+ path?: string;
+ method?: string;
+ url?: string;
+ reqSignedIn?: boolean;
+ reqRole?: string;
+ reqAction?: string;
+ // +listType=atomic
+ headers?: string[];
+ body?: Record;
+ tokenAuth?: {
+ url?: string;
+ // +listType=set
+ scopes?: string[];
+ params?: Record;
+ };
+ jwtTokenAuth?: {
+ url?: string;
+ // +listType=set
+ scopes?: string[];
+ params?: Record;
+ };
+ // +listType=atomic
+ urlParams?: {
+ name?: string;
+ content?: string;
+ }[];
+}
+
+export const defaultRoute = (): Route => ({
+});
+
+export interface IAM {
+ // +listType=atomic
+ permissions?: {
+ action?: string;
+ scope?: string;
+ }[];
+}
+
+export const defaultIAM = (): IAM => ({
+});
+
+export interface Role {
+ role?: {
+ name?: string;
+ description?: string;
+ // +listType=atomic
+ permissions?: {
+ action?: string;
+ scope?: string;
+ }[];
+ };
+ // +listType=set
+ grants?: string[];
+}
+
+export const defaultRole = (): Role => ({
+});
+
+export interface Extensions {
+ // +listType=atomic
+ addedComponents?: {
+ // +listType=set
+ targets: string[];
+ title: string;
+ description?: string;
+ }[];
+ // +listType=atomic
+ addedLinks?: {
+ // +listType=set
+ targets: string[];
+ title: string;
+ description?: string;
+ }[];
+ // +listType=atomic
+ addedFunctions?: {
+ // +listType=set
+ targets: string[];
+ title: string;
+ description?: string;
+ }[];
+ // +listType=set
+ // +listMapKey=id
+ exposedComponents?: {
+ id: string;
+ title?: string;
+ description?: string;
+ }[];
+ // +listType=set
+ // +listMapKey=id
+ extensionPoints?: {
+ id: string;
+ title?: string;
+ description?: string;
+ }[];
+}
+
+export const defaultExtensions = (): Extensions => ({
+});
+
+export interface Spec {
+ pluginJson: JSONData;
+ class: "core" | "external";
+ module?: {
+ path: string;
+ hash?: string;
+ loadingStrategy?: "fetch" | "script";
+ };
+ baseURL?: string;
+ signature?: {
+ status: "internal" | "valid" | "invalid" | "modified" | "unsigned";
+ type?: "grafana" | "commercial" | "community" | "private" | "private-glob";
+ org?: string;
+ };
+ angular?: {
+ detected: boolean;
+ };
+ translations?: Record;
+ // +listType=atomic
+ children?: string[];
+}
+
+export const defaultSpec = (): Spec => ({
+ pluginJson: defaultJSONData(),
+ class: "core",
+});
+
diff --git a/apps/plugins/plugin/src/generated/meta/v0alpha1/types.status.gen.ts b/apps/plugins/plugin/src/generated/meta/v0alpha1/types.status.gen.ts
new file mode 100644
index 00000000000..01be8df7961
--- /dev/null
+++ b/apps/plugins/plugin/src/generated/meta/v0alpha1/types.status.gen.ts
@@ -0,0 +1,30 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+export interface OperatorState {
+ // lastEvaluation is the ResourceVersion last evaluated
+ lastEvaluation: string;
+ // state describes the state of the lastEvaluation.
+ // It is limited to three possible states for machine evaluation.
+ state: "success" | "in_progress" | "failed";
+ // descriptiveState is an optional more descriptive state field which has no requirements on format
+ descriptiveState?: string;
+ // details contains any extra information that is operator-specific
+ details?: Record;
+}
+
+export const defaultOperatorState = (): OperatorState => ({
+ lastEvaluation: "",
+ state: "success",
+});
+
+export interface Status {
+ // operatorStates is a map of operator ID to operator state evaluations.
+ // Any operator which consumes this kind SHOULD add its state evaluation information to this field.
+ operatorStates?: Record;
+ // additionalFields is reserved for future use
+ additionalFields?: Record;
+}
+
+export const defaultStatus = (): Status => ({
+});
+
diff --git a/apps/plugins/plugin/src/generated/plugin/v0alpha1/plugin_object_gen.ts b/apps/plugins/plugin/src/generated/plugin/v0alpha1/plugin_object_gen.ts
new file mode 100644
index 00000000000..c4e625fc418
--- /dev/null
+++ b/apps/plugins/plugin/src/generated/plugin/v0alpha1/plugin_object_gen.ts
@@ -0,0 +1,49 @@
+/*
+ * This file was generated by grafana-app-sdk. DO NOT EDIT.
+ */
+import { Spec } from './types.spec.gen';
+import { Status } from './types.status.gen';
+
+export interface Metadata {
+ name: string;
+ namespace: string;
+ generateName?: string;
+ selfLink?: string;
+ uid?: string;
+ resourceVersion?: string;
+ generation?: number;
+ creationTimestamp?: string;
+ deletionTimestamp?: string;
+ deletionGracePeriodSeconds?: number;
+ labels?: Record;
+ annotations?: Record;
+ ownerReferences?: OwnerReference[];
+ finalizers?: string[];
+ managedFields?: ManagedFieldsEntry[];
+}
+
+export interface OwnerReference {
+ apiVersion: string;
+ kind: string;
+ name: string;
+ uid: string;
+ controller?: boolean;
+ blockOwnerDeletion?: boolean;
+}
+
+export interface ManagedFieldsEntry {
+ manager?: string;
+ operation?: string;
+ apiVersion?: string;
+ time?: string;
+ fieldsType?: string;
+ subresource?: string;
+}
+
+export interface Plugin {
+ kind: string;
+ apiVersion: string;
+ metadata: Metadata;
+ spec: Spec;
+ status: Status;
+}
diff --git a/apps/plugins/plugin/src/generated/plugin/v0alpha1/types.metadata.gen.ts b/apps/plugins/plugin/src/generated/plugin/v0alpha1/types.metadata.gen.ts
new file mode 100644
index 00000000000..4377f3c1d08
--- /dev/null
+++ b/apps/plugins/plugin/src/generated/plugin/v0alpha1/types.metadata.gen.ts
@@ -0,0 +1,30 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+// metadata contains embedded CommonMetadata and can be extended with custom string fields
+// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
+// without external reference as using the CommonMetadata reference breaks thema codegen.
+export interface Metadata {
+ updateTimestamp: string;
+ createdBy: string;
+ uid: string;
+ creationTimestamp: string;
+ deletionTimestamp?: string;
+ finalizers: string[];
+ resourceVersion: string;
+ generation: number;
+ updatedBy: string;
+ labels: Record;
+}
+
+export const defaultMetadata = (): Metadata => ({
+ updateTimestamp: "",
+ createdBy: "",
+ uid: "",
+ creationTimestamp: "",
+ finalizers: [],
+ resourceVersion: "",
+ generation: 0,
+ updatedBy: "",
+ labels: {},
+});
+
diff --git a/apps/plugins/plugin/src/generated/plugin/v0alpha1/types.spec.gen.ts b/apps/plugins/plugin/src/generated/plugin/v0alpha1/types.spec.gen.ts
new file mode 100644
index 00000000000..6b7824b8941
--- /dev/null
+++ b/apps/plugins/plugin/src/generated/plugin/v0alpha1/types.spec.gen.ts
@@ -0,0 +1,13 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+export interface Spec {
+ id: string;
+ version: string;
+ url?: string;
+}
+
+export const defaultSpec = (): Spec => ({
+ id: "",
+ version: "",
+});
+
diff --git a/apps/plugins/plugin/src/generated/plugin/v0alpha1/types.status.gen.ts b/apps/plugins/plugin/src/generated/plugin/v0alpha1/types.status.gen.ts
new file mode 100644
index 00000000000..01be8df7961
--- /dev/null
+++ b/apps/plugins/plugin/src/generated/plugin/v0alpha1/types.status.gen.ts
@@ -0,0 +1,30 @@
+// Code generated - EDITING IS FUTILE. DO NOT EDIT.
+
+export interface OperatorState {
+ // lastEvaluation is the ResourceVersion last evaluated
+ lastEvaluation: string;
+ // state describes the state of the lastEvaluation.
+ // It is limited to three possible states for machine evaluation.
+ state: "success" | "in_progress" | "failed";
+ // descriptiveState is an optional more descriptive state field which has no requirements on format
+ descriptiveState?: string;
+ // details contains any extra information that is operator-specific
+ details?: Record;
+}
+
+export const defaultOperatorState = (): OperatorState => ({
+ lastEvaluation: "",
+ state: "success",
+});
+
+export interface Status {
+ // operatorStates is a map of operator ID to operator state evaluations.
+ // Any operator which consumes this kind SHOULD add its state evaluation information to this field.
+ operatorStates?: Record;
+ // additionalFields is reserved for future use
+ additionalFields?: Record;
+}
+
+export const defaultStatus = (): Status => ({
+});
+
diff --git a/devenv/dev-dashboards/datasource-loki/loki_query_splitting.json b/devenv/dev-dashboards/datasource-loki/loki_query_splitting.json
index 0cc5135a0f4..44577d49ac4 100644
--- a/devenv/dev-dashboards/datasource-loki/loki_query_splitting.json
+++ b/devenv/dev-dashboards/datasource-loki/loki_query_splitting.json
@@ -216,8 +216,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -311,8 +310,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -493,8 +491,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -584,8 +581,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -675,8 +671,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -789,8 +784,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -903,8 +897,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -1018,8 +1011,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
diff --git a/devenv/dev-dashboards/panel-gauge/gauge_tests.json b/devenv/dev-dashboards/panel-gauge/gauge_tests.json
index f32ace420b4..f76f5d8809a 100644
--- a/devenv/dev-dashboards/panel-gauge/gauge_tests.json
+++ b/devenv/dev-dashboards/panel-gauge/gauge_tests.json
@@ -51,17 +51,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -122,17 +119,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -193,17 +187,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -255,7 +246,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -264,17 +254,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -326,7 +313,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -335,17 +321,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -397,7 +380,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -406,17 +388,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -468,7 +447,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [],
"max": 100,
"min": 0,
@@ -477,17 +455,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -641,7 +616,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"from": "",
@@ -660,17 +634,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -723,7 +694,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"from": "",
@@ -742,17 +712,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -805,7 +772,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"from": "0",
@@ -824,17 +790,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -887,7 +850,6 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "",
"mappings": [
{
"from": "0",
@@ -915,17 +877,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -991,7 +950,7 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "2",
+ "decimals": 2,
"mappings": [],
"max": 100,
"min": 0,
@@ -1000,17 +959,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -1071,7 +1027,7 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "2",
+ "decimals": 2,
"mappings": [],
"max": 100,
"min": 0,
@@ -1080,17 +1036,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -1152,7 +1105,7 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "2",
+ "decimals": 2,
"mappings": [],
"max": 100,
"min": 0,
@@ -1161,17 +1114,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
@@ -1233,7 +1183,7 @@
"mode": "thresholds"
},
"custom": {},
- "decimals": "2",
+ "decimals": 2,
"mappings": [],
"max": 100,
"min": 0,
@@ -1242,17 +1192,14 @@
"steps": [
{
"color": "#7EB26D",
- "index": 0,
"value": null
},
{
"color": "#ef843c",
- "index": 1,
"value": 75
},
{
"color": "#e24d42",
- "index": 2,
"value": 90
}
]
diff --git a/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json b/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json
index ff69226fdf5..c44353b7df2 100644
--- a/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json
+++ b/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json
@@ -2067,7 +2067,7 @@
}
],
"title": "Numeric, no series",
- "type": "gauge"
+ "type": "radialbar"
},
{
"datasource": {
@@ -2131,7 +2131,7 @@
}
],
"title": "Non-numeric",
- "type": "gauge"
+ "type": "radialbar"
}
],
"preload": false,
diff --git a/devenv/dev-dashboards/panel-histogram/histogram_tests.json b/devenv/dev-dashboards/panel-histogram/histogram_tests.json
index af6127cb447..7d6a684417e 100644
--- a/devenv/dev-dashboards/panel-histogram/histogram_tests.json
+++ b/devenv/dev-dashboards/panel-histogram/histogram_tests.json
@@ -58,8 +58,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -125,8 +124,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -192,8 +190,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -269,8 +266,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -343,8 +339,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -432,8 +427,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -516,8 +510,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -597,8 +590,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -678,8 +670,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
@@ -759,8 +750,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -824,8 +814,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": [
{
diff --git a/devenv/dev-dashboards/panel-piechart/panel_test_piechart.json b/devenv/dev-dashboards/panel-piechart/panel_test_piechart.json
index ac11fd803b9..4333993ea8e 100644
--- a/devenv/dev-dashboards/panel-piechart/panel_test_piechart.json
+++ b/devenv/dev-dashboards/panel-piechart/panel_test_piechart.json
@@ -248,7 +248,7 @@
"legend": {
"values": ["percent"],
"displayMode": "table",
- "placement": "right"
+ "placement": "bottom"
},
"pieType": "pie",
"reduceOptions": {
@@ -256,7 +256,7 @@
"fields": "",
"values": false
},
- "showLegend": true,
+ "showLegend": false,
"strokeWidth": 1,
"text": {}
},
@@ -272,15 +272,6 @@
"timeFrom": null,
"timeShift": null,
"title": "Percent",
- "transformations": [
- {
- "id": "renameByRegex",
- "options": {
- "regex": "^Backend-(.*)$",
- "renamePattern": "b-$1"
- }
- }
- ],
"type": "piechart"
},
{
@@ -320,7 +311,7 @@
"legend": {
"values": ["value"],
"displayMode": "table",
- "placement": "right"
+ "placement": "bottom"
},
"pieType": "pie",
"reduceOptions": {
@@ -328,7 +319,7 @@
"fields": "",
"values": false
},
- "showLegend": true,
+ "showLegend": false,
"strokeWidth": 1,
"text": {}
},
@@ -344,15 +335,6 @@
"timeFrom": null,
"timeShift": null,
"title": "Value",
- "transformations": [
- {
- "id": "renameByRegex",
- "options": {
- "regex": "(.*)",
- "renamePattern": "$1-how-much-wood-could-a-woodchuck-chuck-if-a-woodchuck-could-chuck-wood"
- }
- }
- ],
"type": "piechart"
},
{
diff --git a/devenv/dev-dashboards/panel-xychart/xychart-tooltip-color-test.json b/devenv/dev-dashboards/panel-xychart/xychart-tooltip-color-test.json
index b0ae2a9d76b..15fda5d6316 100644
--- a/devenv/dev-dashboards/panel-xychart/xychart-tooltip-color-test.json
+++ b/devenv/dev-dashboards/panel-xychart/xychart-tooltip-color-test.json
@@ -62,8 +62,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -150,8 +149,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -238,8 +236,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -326,8 +323,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -421,8 +417,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -516,8 +511,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
@@ -611,8 +605,7 @@
"value": 80
}
]
- },
- "unitScale": true
+ }
},
"overrides": []
},
diff --git a/docs/sources/administration/roles-and-permissions/_index.md b/docs/sources/administration/roles-and-permissions/_index.md
index c8135836fa1..7a33d940a15 100644
--- a/docs/sources/administration/roles-and-permissions/_index.md
+++ b/docs/sources/administration/roles-and-permissions/_index.md
@@ -35,10 +35,10 @@ For Grafana Cloud users, Grafana Support is not authorised to make org role chan
## Grafana server administrators
-A Grafana server administrator manages server-wide settings and access to resources such as organizations, users, and licenses. Grafana includes a default server administrator that you can use to manage all of Grafana, or you can divide that responsibility among other server administrators that you create.
+A Grafana server administrator (sometimes referred to as a **Grafana Admin**) manages server-wide settings and access to resources such as organizations, users, and licenses. Grafana includes a default server administrator that you can use to manage all of Grafana, or you can divide that responsibility among other server administrators that you create.
-{{< admonition type="note" >}}
-The server administrator role does not mean that the user is also a Grafana [organization administrator](#organization-roles).
+{{< admonition type="caution" >}}
+The server administrator role is distinct from the [organization administrator](#organization-roles) role.
{{< /admonition >}}
A server administrator can perform the following tasks:
@@ -50,7 +50,7 @@ A server administrator can perform the following tasks:
- Upgrade the server to Grafana Enterprise.
{{< admonition type="note" >}}
-The server administrator role does not exist in Grafana Cloud.
+The server administrator (Grafana Admin) role does not exist in Grafana Cloud.
{{< /admonition >}}
To assign or remove server administrator privileges, see [Server user management](../user-management/server-user-management/assign-remove-server-admin-privileges/).
diff --git a/docs/sources/administration/roles-and-permissions/access-control/manage-rbac-roles/index.md b/docs/sources/administration/roles-and-permissions/access-control/manage-rbac-roles/index.md
index 40a6d3645af..b0f35087efc 100644
--- a/docs/sources/administration/roles-and-permissions/access-control/manage-rbac-roles/index.md
+++ b/docs/sources/administration/roles-and-permissions/access-control/manage-rbac-roles/index.md
@@ -53,6 +53,11 @@ refs:
destination: /docs/grafana//administration/roles-and-permissions/access-control/custom-role-actions-scopes/
- pattern: /docs/grafana-cloud/
destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/custom-role-actions-scopes/
+ rbac-terraform-provisioning:
+ - pattern: /docs/grafana/
+ destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-terraform-provisioning/
+ - pattern: /docs/grafana-cloud/
+ destination: /docs/grafana-cloud/account-management/authentication-and-permissions/access-control/rbac-terraform-provisioning/
rbac-grafana-provisioning:
- pattern: /docs/grafana/
destination: /docs/grafana//administration/roles-and-permissions/access-control/rbac-grafana-provisioning/
@@ -145,7 +150,13 @@ Refer to the [RBAC HTTP API](ref:api-rbac-get-a-role) for more details.
## Create custom roles
-This section shows you how to create a custom RBAC role using Grafana provisioning and the HTTP API.
+This section shows you how to create a custom RBAC role using Grafana provisioning or the HTTP API.
+
+Creating and editing custom roles is not currently possible in the Grafana UI. To manage custom roles, use one of the following methods:
+
+- [Provisioning](ref:rbac-grafana-provisioning) (for self-managed instances)
+- [HTTP API](ref:api-rbac-create-a-new-custom-role)
+- [Terraform](ref:rbac-terraform-provisioning)
Create a custom role when basic roles and fixed roles do not meet your permissions requirements.
@@ -153,14 +164,101 @@ Create a custom role when basic roles and fixed roles do not meet your permissio
- [Plan your RBAC rollout strategy](ref:plan-rbac-rollout-strategy).
- Determine which permissions you want to add to the custom role. To see a list of actions and scope, refer to [RBAC permissions, actions, and scopes](ref:custom-role-actions-scopes).
-- [Enable role provisioning](ref:rbac-grafana-provisioning).
- Ensure that you have permissions to create a custom role.
- By default, the Grafana Admin role has permission to create custom roles.
- A Grafana Admin can delegate the custom role privilege to another user by creating a custom role with the relevant permissions and adding the `permissions:type:delegate` scope.
-### Create custom roles using provisioning
+### Create custom roles using the HTTP API
-[File-based provisioning](ref:rbac-grafana-provisioning) is one method you can use to create custom roles.
+The following examples show you how to create a custom role using the Grafana HTTP API. For more information about the HTTP API, refer to [Create a new custom role](ref:api-rbac-create-a-new-custom-role).
+
+{{< admonition type="note" >}}
+When you create a custom role you can only give it the same permissions you already have. For example, if you only have `users:create` permissions, then you can't create a role that includes other permissions.
+{{< /admonition >}}
+
+The following example creates a `custom:users:admin` role and assigns the `users:create` action to it.
+
+**Example request**
+
+```
+curl --location --request POST '/api/access-control/roles/' \
+--header 'Authorization: Basic YWRtaW46cGFzc3dvcmQ=' \
+--header 'Content-Type: application/json' \
+--data-raw '{
+ "version": 1,
+ "uid": "jZrmlLCkGksdka",
+ "name": "custom:users:admin",
+ "displayName": "custom users admin",
+ "description": "My custom role which gives users permissions to create users",
+ "global": true,
+ "permissions": [
+ {
+ "action": "users:create"
+ }
+ ]
+}'
+```
+
+**Example response**
+
+```
+{
+ "version": 1,
+ "uid": "jZrmlLCkGksdka",
+ "name": "custom:users:admin",
+ "displayName": "custom users admin",
+ "description": "My custom role which gives users permissions to create users",
+ "global": true,
+ "permissions": [
+ {
+ "action": "users:create"
+ "updated": "2021-05-17T22:07:31.569936+02:00",
+ "created": "2021-05-17T22:07:31.569935+02:00"
+ }
+ ],
+ "updated": "2021-05-17T22:07:31.564403+02:00",
+ "created": "2021-05-17T22:07:31.564403+02:00"
+}
+```
+
+Refer to the [RBAC HTTP API](ref:api-rbac-create-a-new-custom-role) for more details.
+
+### Create custom roles using Terraform
+
+You can use the [Grafana Terraform provider](https://registry.terraform.io/providers/grafana/grafana/latest/docs) to manage custom roles and their assignments. This is the recommended method for Grafana Cloud users who want to manage RBAC as code. For more information, refer to [Provisioning RBAC with Terraform](ref:rbac-terraform-provisioning).
+
+The following example creates a custom role and assigns it to a team:
+
+```terraform
+resource "grafana_role" "custom_folder_manager" {
+ name = "custom:folders:manager"
+ description = "Custom role for reading and creating folders"
+ uid = "custom-folders-manager"
+ version = 1
+ global = true
+
+ permissions {
+ action = "folders:read"
+ scope = "folders:*"
+ }
+
+ permissions {
+ action = "folders:create"
+ scope = "folders:uid:general" # Allows creating folders at the root level
+ }
+}
+
+resource "grafana_role_assignment" "custom_folder_manager_assignment" {
+ role_uid = grafana_role.custom_folder_manager.uid
+ teams = [""]
+}
+```
+
+For more information, refer to the [`grafana_role`](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/role) and [`grafana_role_assignment`](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/role_assignment) documentation in the Terraform Registry.
+
+### Create custom roles using file-based provisioning
+
+You can use [file-based provisioning](ref:rbac-grafana-provisioning) to create custom roles for self-managed instances.
1. Open the YAML configuration file and locate the `roles` section.
@@ -251,61 +349,6 @@ roles:
state: 'absent'
```
-### Create custom roles using the HTTP API
-
-The following examples show you how to create a custom role using the Grafana HTTP API. For more information about the HTTP API, refer to [Create a new custom role](ref:api-rbac-create-a-new-custom-role).
-
-{{< admonition type="note" >}}
-You cannot create a custom role with permissions that you do not have. For example, if you only have `users:create` permissions, then you cannot create a role that includes other permissions.
-{{< /admonition >}}
-
-The following example creates a `custom:users:admin` role and assigns the `users:create` action to it.
-
-**Example request**
-
-```
-curl --location --request POST '/api/access-control/roles/' \
---header 'Authorization: Basic YWRtaW46cGFzc3dvcmQ=' \
---header 'Content-Type: application/json' \
---data-raw '{
- "version": 1,
- "uid": "jZrmlLCkGksdka",
- "name": "custom:users:admin",
- "displayName": "custom users admin",
- "description": "My custom role which gives users permissions to create users",
- "global": true,
- "permissions": [
- {
- "action": "users:create"
- }
- ]
-}'
-```
-
-**Example response**
-
-```
-{
- "version": 1,
- "uid": "jZrmlLCkGksdka",
- "name": "custom:users:admin",
- "displayName": "custom users admin",
- "description": "My custom role which gives users permissions to create users",
- "global": true,
- "permissions": [
- {
- "action": "users:create"
- "updated": "2021-05-17T22:07:31.569936+02:00",
- "created": "2021-05-17T22:07:31.569935+02:00"
- }
- ],
- "updated": "2021-05-17T22:07:31.564403+02:00",
- "created": "2021-05-17T22:07:31.564403+02:00"
-}
-```
-
-Refer to the [RBAC HTTP API](ref:api-rbac-create-a-new-custom-role) for more details.
-
## Update basic role permissions
If the default basic role definitions do not meet your requirements, you can change their permissions.
diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md
index 15cf895a5e8..465880919ca 100644
--- a/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md
+++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md
@@ -66,17 +66,18 @@ Please refer to plugin documentation to see what RBAC permissions the plugin has
The following list contains app plugins that have fine-grained RBAC support.
-| App plugin | App plugin ID | App plugin permission documentation |
-| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| [Access policies](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/access-policies/) | `grafana-auth-app` | [RBAC actions for Access Policies](ref:cloud-access-policies-action-definitions) |
-| [Adaptive Metrics](https://grafana.com/docs/grafana-cloud/cost-management-and-billing/reduce-costs/metrics-costs/control-metrics-usage-via-adaptive-metrics/adaptive-metrics-plugin/) | `grafana-adaptive-metrics-app` | [RBAC actions for Adaptive Metrics](ref:adaptive-metrics-permissions) |
-| [Cloud Provider](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/monitor-cloud-provider/) | `grafana-csp-app` | [Cloud Provider Observability role-based access control](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/monitor-cloud-provider/rbac/) |
-| [Incident](https://grafana.com/docs/grafana-cloud/alerting-and-irm/irm/incident/) | `grafana-incident-app` | n/a |
-| [Kubernetes Monitoring](/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/) | `grafana-k8s-app` | [Kubernetes Monitoring role-based access control](/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/configuration/control-access/#precision-access-with-rbac-custom-plugin-roles) |
-| [OnCall](https://grafana.com/docs/grafana-cloud/alerting-and-irm/irm/oncall/) | `grafana-oncall-app` | [Configure RBAC for OnCall](https://grafana.com/docs/grafana-cloud/alerting-and-irm/irm/oncall/manage/user-and-team-management/#manage-users-and-teams-for-grafana-oncall) |
-| [Performance Testing (K6)](https://grafana.com/docs/grafana-cloud/testing/k6/) | `k6-app` | [Configure RBAC for K6](https://grafana.com/docs/grafana-cloud/testing/k6/projects-and-users/configure-rbac/) |
-| [Private data source connect (PDC)](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/) | `grafana-pdc-app` | n/a |
-| [Service Level Objective (SLO)](https://grafana.com/docs/grafana-cloud/alerting-and-irm/slo/) | `grafana-slo-app` | [Configure RBAC for SLO](https://grafana.com/docs/grafana-cloud/alerting-and-irm/slo/set-up/rbac/) |
+| App plugin | App plugin ID | App plugin permission documentation |
+| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| [Access policies](https://grafana.com/docs/grafana-cloud/account-management/authentication-and-permissions/access-policies/) | `grafana-auth-app` | [RBAC actions for Access Policies](ref:cloud-access-policies-action-definitions) |
+| [Adaptive Metrics](https://grafana.com/docs/grafana-cloud/cost-management-and-billing/reduce-costs/metrics-costs/control-metrics-usage-via-adaptive-metrics/adaptive-metrics-plugin/) | `grafana-adaptive-metrics-app` | [RBAC actions for Adaptive Metrics](ref:adaptive-metrics-permissions) |
+| [Cloud Provider](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/monitor-cloud-provider/) | `grafana-csp-app` | [Cloud Provider Observability role-based access control](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/monitor-cloud-provider/rbac/) |
+| [Incident](https://grafana.com/docs/grafana-cloud/alerting-and-irm/irm/incident/) | `grafana-incident-app` | n/a |
+| [Kubernetes Monitoring](/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/) | `grafana-k8s-app` | [Kubernetes Monitoring role-based access control](/docs/grafana-cloud/monitor-infrastructure/kubernetes-monitoring/configuration/control-access/#precision-access-with-rbac-custom-plugin-roles) |
+| [OnCall](https://grafana.com/docs/grafana-cloud/alerting-and-irm/irm/oncall/) | `grafana-oncall-app` | [Configure RBAC for OnCall](https://grafana.com/docs/grafana-cloud/alerting-and-irm/irm/oncall/manage/user-and-team-management/#manage-users-and-teams-for-grafana-oncall) |
+| [Performance Testing (K6)](https://grafana.com/docs/grafana-cloud/testing/k6/) | `k6-app` | [Configure RBAC for K6](https://grafana.com/docs/grafana-cloud/testing/k6/projects-and-users/configure-rbac/) |
+| [Private data source connect (PDC)](https://grafana.com/docs/grafana-cloud/connect-externally-hosted/private-data-source-connect/) | `grafana-pdc-app` | n/a |
+| [Service Level Objective (SLO)](https://grafana.com/docs/grafana-cloud/alerting-and-irm/slo/) | `grafana-slo-app` | [Configure RBAC for SLO](https://grafana.com/docs/grafana-cloud/alerting-and-irm/slo/set-up/rbac/) |
+| [Synthetic Monitoring](https://grafana.com/docs/grafana-cloud/testing/synthetic-monitoring/) | `grafana-synthetic-monitoring-app` | [Configure RBAC for Synthetic Monitoring](https://grafana.com/docs/grafana-cloud/testing/synthetic-monitoring/user-and-team-management/) |
### Revoke fine-grained access from app plugins
diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-grafana-provisioning/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-grafana-provisioning/index.md
index 06f9699533b..fe45a620bc5 100644
--- a/docs/sources/administration/roles-and-permissions/access-control/rbac-grafana-provisioning/index.md
+++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-grafana-provisioning/index.md
@@ -6,7 +6,6 @@ description: Learn about RBAC Grafana provisioning and view an example YAML prov
file that configures Grafana role assignments.
labels:
products:
- - cloud
- enterprise
menuTitle: Provisioning RBAC with Grafana
title: Provisioning RBAC with Grafana
@@ -52,11 +51,13 @@ refs:
# Provisioning RBAC with Grafana
{{< admonition type="note" >}}
-Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud).
+Available in [Grafana Enterprise](/docs/grafana//introduction/grafana-enterprise/) for self-managed instances. This feature is not available in Grafana Cloud.
{{< /admonition >}}
You can create, change or remove [Custom roles](ref:manage-rbac-roles-create-custom-roles-using-provisioning) and create or remove [basic role assignments](ref:assign-rbac-roles-assign-a-fixed-role-to-a-basic-role-using-provisioning), by adding one or more YAML configuration files in the `provisioning/access-control/` directory.
+Because this method requires access to the file system where Grafana is running, it's only available for self-managed Grafana instances. To provision RBAC in Grafana Cloud, use [Terraform](ref:rbac-terraform-provisioning) or the [HTTP API](ref:api-rbac-create-and-manage-custom-roles).
+
Grafana performs provisioning during startup. After you make a change to the configuration file, you can reload it during runtime. You do not need to restart the Grafana server for your changes to take effect.
**Before you begin:**
diff --git a/docs/sources/as-code/observability-as-code/_index.md b/docs/sources/as-code/observability-as-code/_index.md
index 338d9235255..92441bd5110 100644
--- a/docs/sources/as-code/observability-as-code/_index.md
+++ b/docs/sources/as-code/observability-as-code/_index.md
@@ -25,10 +25,6 @@ cards:
height: 24
href: ./foundation-sdk/
description: The Grafana Foundation SDK is a set of tools, types, and libraries that let you define Grafana dashboards and resources using familiar programming languages like Go, TypeScript, Python, Java, and PHP. Use it in conjunction with `grafanactl` to push your programmatically generated resources.
- - title: JSON schema v2
- height: 24
- href: ./schema-v2/
- description: Grafana dashboards are represented as JSON objects that store metadata, panels, variables, and settings. Observability as Code works with all versions of the JSON model, and it's fully compatible with version 2.
- title: Git Sync (private preview)
height: 24
href: ./provision-resources/intro-git-sync/
@@ -68,7 +64,7 @@ Historically, managing Grafana as code involved various community and Grafana La
- This approach requires handling HTTP requests and responses but provides complete control over resource management.
- `grafanactl`, Git Sync, and the Foundation SDK are all built on top of these APIs.
-- To understand Dashboard Schemas accepted by the APIs, refer to the [JSON models documentation](https://grafana.com/docs/grafana//observability-as-code/schema-v2/).
+- To understand Dashboard Schemas accepted by the APIs, refer to the [JSON models documentation](https://grafana.com/docs/grafana//visualizations/dashboards/build-dashboards/view-dashboard-json-model/index.md).
## Explore
diff --git a/docs/sources/as-code/observability-as-code/schema-v2/_index.md b/docs/sources/as-code/observability-as-code/schema-v2/_index.md
deleted file mode 100644
index 65c73a49cbe..00000000000
--- a/docs/sources/as-code/observability-as-code/schema-v2/_index.md
+++ /dev/null
@@ -1,243 +0,0 @@
----
-description: A reference for the JSON dashboard schemas used with Observability as Code, including the experimental V2 schema.
-keywords:
- - configuration
- - as code
- - dashboards
- - git integration
- - git sync
- - github
-labels:
- products:
- - cloud
- - enterprise
- - oss
-title: JSON schema v2
-weight: 500
-canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/
-aliases:
- - ../../observability-as-code/schema-v2/ # /docs/grafana/next/observability-as-code/schema-v2/
----
-
-# Dashboard JSON schema v2
-
-{{< admonition type="caution" >}}
-
-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.**
-
-{{< /admonition >}}
-
-Grafana dashboards are represented as JSON objects that store metadata, panels, variables, and settings.
-
-Observability as Code works with all versions of the JSON model, and it's fully compatible with version 2.
-
-## Before you begin
-
-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).
-
-## Accessing the JSON Model
-
-To view the JSON representation of a dashboard:
-
-1. Toggle on the edit mode switch in the top-right corner of the dashboard.
-1. Click the gear icon in the top navigation bar to go to **Settings**.
-1. Select the **JSON Model** tab.
-1. Copy or edit the JSON structure as needed.
-
-## JSON fields
-
-```json
-{
- "annotations": [],
- "cursorSync": "Off",
- "editable": true,
- "elements": {},
- "layout": {
- "kind": GridLayout, // Can also be AutoGridLayout, RowsLayout, or TabsLayout
- "spec": {
- "items": []
- }
- },
- "links": [],
- "liveNow": false,
- "preload": false,
- "tags": [], // Tags associated with the dashboard.
- "timeSettings": {
- "autoRefresh": "",
- "autoRefreshIntervals": [
- "5s",
- "10s",
- "30s",
- "1m",
- "5m",
- "15m",
- "30m",
- "1h",
- "2h",
- "1d"
- ],
- "fiscalYearStartMonth": 0,
- "from": "now-6h",
- "hideTimepicker": false,
- "timezone": "browser",
- "to": "now"
- },
- "title": "",
- "variables": []
-},
-```
-
-The dashboard JSON sample shown uses the default `GridLayoutKind`.
-The JSON in a new dashboard for the other three layout options, `AutoGridLayout`, `RowsLayout`, and `TabsLayout`, are as follows:
-
-**`AutoGridLayout`**
-
-```json
- "layout": {
- "kind": "AutoGridLayout",
- "spec": {
- "columnWidthMode": "standard",
- "items": [],
- "fillScreen": false,
- "maxColumnCount": 3,
- "rowHeightMode": "standard"
- }
- },
-```
-
-**`RowsLayout`**
-
-```json
- "layout": {
- "kind": "RowsLayout",
- "spec": {
- "rows": []
- },
-```
-
-**`TabsLayout`**
-
-```json
- "layout": {
- "kind": "TabsLayout",
- "spec": {
- "tabs": []
- },
-```
-
-### `DashboardSpec`
-
-The following table explains the usage of the dashboard JSON fields.
-The table includes default and other fields:
-
-
-
-| Name | Usage |
-| ------------ | ------------------------------------------------------------------------- |
-| annotations | Contains the list of annotations that are associated with the dashboard. |
-| cursorSync | Dashboard cursor sync behavior.
`Off` - No shared crosshair or tooltip (default)
`Crosshair` - Shared crosshair
`Tooltip` - Shared crosshair and shared tooltip
|
-| editable | bool. Whether or not a dashboard is editable. |
-| elements | Contains the list of elements included in the dashboard. Supported dashboard elements are: PanelKind and LibraryPanelKind. |
-| layout | The dashboard layout. Supported layouts are:
GridLayoutKind
AutoGridLayoutKind
RowsLayoutKind
TabsLayoutKind
|
-| links | Links with references to other dashboards or external websites. |
-| liveNow | bool. When set to `true`, the dashboard redraws panels at an interval matching the pixel width. This keeps data "moving left" regardless of the query refresh rate. This setting helps avoid dashboards presenting stale live data. |
-| preload | bool. When set to `true`, the dashboard loads all panels when the dashboard is loaded. |
-| tags | Contains the list of tags associated with dashboard. |
-| timeSettings | All time settings for the dashboard. |
-| title | Title of the dashboard. |
-| variables | Contains the list of configured template variables. |
-
-
-
-### `annotations`
-
-The configuration for the list of annotations that are associated with the dashboard.
-For the JSON and field usage notes, refer to the [annotations schema documentation](https://grafana.com/docs/grafana//observability-as-code/schema-v2/annotations-schema/).
-
-### `elements`
-
-Dashboards can contain the following elements:
-
-- [PanelKind](https://grafana.com/docs/grafana//observability-as-code/schema-v2/panel-schema/)
-- [LibraryPanelKind](https://grafana.com/docs/grafana//observability-as-code/schema-v2/librarypanel-schema/)
-
-### `layout`
-
-Dashboards can have four layout options:
-
-- [GridLayoutKind](https://grafana.com/docs/grafana//observability-as-code/schema-v2/layout-schema/#gridlayoutkind)
-- [AutoGridLayoutKind](https://grafana.com/docs/grafana//observability-as-code/schema-v2/layout-schema/#autogridlayoutkind)
-- [RowsLayoutKind](https://grafana.com/docs/grafana//observability-as-code/schema-v2/layout-schema/#rowslayoutkind)
-- [TabsLayoutKind](https://grafana.com/docs/grafana//observability-as-code/schema-v2/layout-schema/#tabslayoutkind)
-
-For the JSON and field usage notes about each of these, refer to the [layout schema documentation](https://grafana.com/docs/grafana//observability-as-code/schema-v2/layout-schema/).
-
-### `links`
-
-The configuration for links with references to other dashboards or external websites.
-
-For the JSON and field usage notes, refer to the [links schema documentation](https://grafana.com/docs/grafana//observability-as-code/schema-v2/links-schema/).
-
-### `tags`
-
-Tags associated with the dashboard. Each tag can be up to 50 characters long.
-
-` [...string]`
-
-### `timesettings`
-
-The `TimeSettingsSpec` defines the default time configuration for the time picker and the refresh picker for the specific dashboard.
-For the JSON and field usage notes about the `TimeSettingsSpec`, refer to the [timesettings schema documentation](https://grafana.com/docs/grafana//observability-as-code/schema-v2/timesettings-schema/).
-
-### `variables`
-
-The `variables` schema defines which variables are used in the dashboard.
-
-There are eight variables types:
-
-- QueryVariableKind
-- TextVariableKind
-- ConstantVariableKind
-- DatasourceVariableKind
-- IntervalVariableKind
-- CustomVariableKind
-- GroupByVariableKind
-- AdhocVariableKind
-
-For the JSON and field usage notes about the `variables` spec, refer to the [variables schema documentation](https://grafana.com/docs/grafana//observability-as-code/schema-v2/variables-schema/).
-
-## Notes and limitations
-
-### Existing dashboards
-
-With schema v2 enabled, you can still open and view your pre-existing dashboards.
-Upon saving, they’ll be updated to the new schema where you can take advantage of the new features and functionalities.
-
-### Dashboard behavior with disabled feature flags
-
-If you disable the Dynamic dashboards or `kubernetesDashboards` feature flags, you should be aware of how dashboards will behave.
-
-#### Disable Dynamic dashboards
-
-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 Dynamic dashboards and `kubernetesDashboards`
-
-You’ll be unable to view or edit dashboards created or updated in the new schema.
-
-### Import and export
-
-From the UI, dashboards created on schema v2 can be exported and imported like other dashboards.
-When you export them to use in another instance, references of data sources are not persisted but data source types are.
-You’ll have the option to select the data source of your choice in the import UI.
diff --git a/docs/sources/as-code/observability-as-code/schema-v2/annotations-schema.md b/docs/sources/as-code/observability-as-code/schema-v2/annotations-schema.md
deleted file mode 100644
index e99e7c2cce6..00000000000
--- a/docs/sources/as-code/observability-as-code/schema-v2/annotations-schema.md
+++ /dev/null
@@ -1,86 +0,0 @@
----
-description: A reference for the JSON annotations schema used with Observability as Code.
-keywords:
- - configuration
- - as code
- - as-code
- - dashboards
- - git integration
- - git sync
- - github
- - annotations
-labels:
- products:
- - cloud
- - enterprise
- - oss
-menuTitle: annotations schema
-title: annotations
-weight: 100
-canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/annotations-schema/
-aliases:
- - ../../../observability-as-code/schema-v2/annotations-schema/ # /docs/grafana/next/observability-as-code/schema-v2/annotations-schema/
----
-
-# `annotations`
-
-The configuration for the list of annotations that are associated with the dashboard.
-
-```json
- "annotations": [
- {
- "kind": "AnnotationQuery",
- "spec": {
- "builtIn": false,
- "datasource": {
- "type": "",
- "uid": ""
- },
- "enable": false,
- "hide": false,
- "iconColor": "",
- "name": ""
- }
- }
- ],
-```
-
-`AnnotationsQueryKind` consists of:
-
-- kind: "AnnotationQuery"
-- spec: [AnnotationQuerySpec](#annotationqueryspec)
-
-## `AnnotationQuerySpec`
-
-| Name | Type/Definition |
-| ---------- | ----------------------------------------------------------------- |
-| datasource | [`DataSourceRef`](#datasourceref) |
-| query | [`DataQueryKind`](#dataquerykind) |
-| enable | bool |
-| hide | bool |
-| iconColor | string |
-| name | string |
-| builtIn | bool. Default is `false`. |
-| filter | [`AnnotationPanelFilter`](#annotationpanelfilter) |
-| options | `[string]`: A catch-all field for datasource-specific properties. |
-
-### `DataSourceRef`
-
-| Name | Usage |
-| ----- | ---------------------------------- |
-| type? | string. The plugin type-id. |
-| uid? | The specific data source instance. |
-
-### `DataQueryKind`
-
-| Name | Type |
-| ---- | ------ |
-| kind | string |
-| spec | string |
-
-### `AnnotationPanelFilter`
-
-| Name | Type/Definition |
-| -------- | ------------------------------------------------------------------------------ |
-| exclude? | bool. Should the specified panels be included or excluded. Default is `false`. |
-| ids | `[...uint8]`. Panel IDs that should be included or excluded. |
diff --git a/docs/sources/as-code/observability-as-code/schema-v2/layout-schema.md b/docs/sources/as-code/observability-as-code/schema-v2/layout-schema.md
deleted file mode 100644
index ca31417bbcf..00000000000
--- a/docs/sources/as-code/observability-as-code/schema-v2/layout-schema.md
+++ /dev/null
@@ -1,339 +0,0 @@
----
-description: A reference for the JSON layout schema used with Observability as Code.
-keywords:
- - configuration
- - as code
- - as-code
- - dashboards
- - git integration
- - git sync
- - github
- - layout
-labels:
- products:
- - cloud
- - enterprise
- - oss
-menuTitle: layout schema
-title: layout
-weight: 400
-canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/layout-schema/
-aliases:
- - ../../../observability-as-code/schema-v2/layout-schema/ # /docs/grafana/next/observability-as-code/schema-v2/layout-schema/
----
-
-# `layout`
-
-There are four layout options offering two types of panel control:
-
-**Panel layout options**
-
-These options control the size and position of panels:
-
-- [GridLayoutKind](#gridlayoutkind) - Corresponds to the **Custom** option in the UI. You define panel size and panel positions using x- and y- settings.
-- [AutoGridLayoutKind](#autogridlayoutkind) - Corresponds to the **Auto grid** option in the UI. Panel size and position are automatically set based on column and row parameters.
-
-**Panel grouping options**
-
-These options control the grouping of panels:
-
-- [RowsLayoutKind](#rowslayoutkind) - Groups panels into rows.
-- [TabsLayoutKind](#tabslayoutkind) - Groups panels into tabs.
-
-## `GridLayoutKind`
-
-The grid layout allows you to manually size and position grid items by setting the height, width, x, and y of each item.
-This layout corresponds to the **Custom** option in the UI.
-
-Following is the JSON for a default grid layout, a grid layout item, and a grid layout row:
-
-```json
- "kind": "GridLayout",
- "spec": {
- "items": [
- {
- "kind": "GridLayoutItem",
- "spec": {
- "element": {...},
- "height": 0,
- "width": 0,
- "x": 0,
- "y": 0
- }
- },
- {
- "kind": "GridLayoutRow",
- "spec": {
- "collapsed": false,
- "elements": [],
- "title": "",
- "y": 0
- }
- },
- ]
- }
-```
-
-`GridLayoutKind` consists of:
-
-- kind: "GridLayout"
-- spec: GridLayoutSpec
- - items: GridLayoutItemKind` or GridLayoutRowKind`
- - GridLayoutItemKind
- - kind: "GridLayoutItem"
- - spec: [GridLayoutItemSpec](#gridlayoutitemspec)
- - GridLayoutRowKind
- - kind: "GridLayoutRow"
- - spec: [GridLayoutRowSpec](#gridlayoutrowspec)
-
-### `GridLayoutItemSpec`
-
-The following table explains the usage of the grid layout item JSON fields:
-
-| Name | Usage |
-| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| x | integer. Position of the item x-axis. |
-| y | integer. Position of the item y-axis. |
-| width | Width of the item in pixels. |
-| height | Height of the item in pixels. |
-| element | `ElementReference`. Reference to a [`PanelKind`](https://grafana.com/docs/grafana//observability-as-code/schema-v2/panel-schema/) from `dashboard.spec.elements` expressed as JSON Schema reference. |
-| repeat? | [RepeatOptions](#repeatoptions). Configured repeat options, if any |
-
-#### `RepeatOptions`
-
-The following table explains the usage of the repeat option JSON fields:
-
-| Name | Usage |
-| ---------- | ---------------------------------------------------- |
-| mode | `RepeatMode` - "variable" |
-| value | string |
-| direction? | Options are `h` for horizontal and `v` for vertical. |
-| maxPerRow? | integer |
-
-### `GridLayoutRowSpec`
-
-The following table explains the usage of the grid layout row JSON fields:
-
-
-
-| Name | Usage |
-| ---- | ----- |
-| y | integer. Position of the row y-axis |
-| collapsed | bool. Whether or not the row is collapsed |
-| title | Row title |
-| elements | [`[...GridLayoutItemKind]`](#gridlayoutitemspec). Grid items in the row will have their y value be relative to the row's y value. This means a panel positioned at `y: 0` in a row with `y: 10` will be positioned at `y: 11` (row header has a height of 1) in the dashboard. |
-| repeat? | [RowRepeatOptions](#rowrepeatoptions) Configured row repeat options, if any
|
-
-
-
-#### `RowRepeatOptions`
-
-| Name | Usage |
-| ----- | ------------------------- |
-| mode | `RepeatMode` - "variable" |
-| value | string |
-
-## `AutoGridLayoutKind`
-
-With an auto grid, Grafana sizes and positions your panels for the best fit based on the column and row constraints that you set.
-This layout corresponds to the **Auto grid** option in the UI.
-
-Following is the JSON for a default auto grid layout and a grid layout item:
-
-
-
-```json
- "kind": "AutoGridLayout",
- "spec": {
- "columnWidthMode": "standard",
- "fillScreen": false,
- "items": [
- {
- "kind": "AutoGridLayoutItem",
- "spec": {
- "element": {...},
- }
- }
- ],
- "maxColumnCount": 3,
- "rowHeightMode": "standard"
- }
-```
-
-`AutoGridLayoutKind` consists of:
-
-- kind: "AutoGridLayout"
-- spec: [AutoGridLayoutSpec](#autogridlayoutspec)
-
-### `AutoGridLayoutSpec`
-
-The following table explains the usage of the auto grid layout JSON fields:
-
-
-
-| Name | Usage |
-| ---- | ----- |
-| maxColumnCount? | number. Default is `3`. |
-| columnWidthMode | Options are: `narrow`, `standard`, `wide`, and `custom`. Default is `standard`. |
-| columnWidth? | number |
-| rowHeightMode | Options are: `short`, `standard`, `tall`, and `custom`. Default is `standard`. |
-| rowHeight? | number |
-| fillScreen? | bool. Default is `false`. |
-| items | `AutoGridLayoutItemKind`. Consists of:
|
-
-
-
-#### `AutoGridLayoutItemSpec`
-
-The following table explains the usage of the auto grid layout item JSON fields:
-
-
-
-| Name | Usage |
-| ---- | ----- |
-| element | `ElementReference`. Reference to a [`PanelKind`](https://grafana.com/docs/grafana//observability-as-code/schema-v2/panel-schema/) from `dashboard.spec.elements` expressed as JSON Schema reference. |
-| repeat? | [AutoGridRepeatOptions](#autogridrepeatoptions). Configured repeat options, if any. |
-| conditionalRendering? | `ConditionalRenderingGroupKind`. Rules for hiding or showing panels, if any. Consists of:
|
-
-
-
-###### `ConditionalRenderingVariableSpec`
-
-| Name | Usage |
-| -------- | ------------------------------------ |
-| variable | string |
-| operator | Options are `equals` and `notEquals` |
-| value | string |
-
-###### `ConditionalRenderingDataSpec`
-
-| Name | Type |
-| ----- | ---- |
-| value | bool |
-
-###### `ConditionalRenderingTimeRangeSizeSpec`
-
-| Name | Type |
-| ----- | ------ |
-| value | string |
-
-## `RowsLayoutKind`
-
-The `RowsLayoutKind` is one of two options that you can use to group panels.
-You can nest any other kind of layout inside a layout row.
-Rows can also be nested in auto grids or tabs.
-
-Following is the JSON for a default rows layout row:
-
-```json
- "kind": "RowsLayout",
- "spec": {
- "rows": [
- {
- "kind": "RowsLayoutRow",
- "spec": {
- "layout": {
- "kind": "GridLayout", // Can also be AutoGridLayout or TabsLayout
- "spec": {...}
- },
- "title": ""
- }
- }
- ]
- }
-```
-
-`RowsLayoutKind` consists of:
-
-- kind: RowsLayout
-- spec: RowsLayoutSpec
- - rows: RowsLayoutRowKind
- - kind: RowsLayoutRow
- - spec: [RowsLayoutRowSpec](#rowslayoutrowspec)
-
-### `RowsLayoutRowSpec`
-
-The following table explains the usage of the rows layout row JSON fields:
-
-
-
-| Name | Usage |
-| ---- | ----- |
-| title? | Title of the row. |
-| collapse | bool. Whether or not the row is collapsed. |
-| hideHeader? | bool. Whether the row header is hidden or shown. |
-| fullScreen? | bool. Whether or not the row takes up the full screen. |
-| conditionalRendering? | `ConditionalRenderingGroupKind`. Rules for hiding or showing rows, if any. Consists of:
|
-
-
-
-## `TabsLayoutKind`
-
-The `TabsLayoutKind` is one of two options that you can use to group panels.
-You can nest any other kind of layout inside a tab.
-Tabs can also be nested in auto grids or rows.
-
-Following is the JSON for a default tabs layout tab and a tab:
-
-```json
- "kind": "TabsLayout",
- "spec": {
- "tabs": [
- {
- "kind": "TabsLayoutTab",
- "spec": {
- "layout": {
- "kind": "GridLayout", // Can also be AutoGridLayout or RowsLayout
- "spec": {...}
- },
- "title": "New tab"
- }
- }
- ]
- }
-```
-
-`TabsLayoutKind` consists of:
-
-- kind: TabsLayout
- - spec: TabsLayoutSpec
- - tabs: TabsLayoutTabKind
- - kind: TabsLayoutTab
- - spec: [TabsLayoutTabSpec](#tabslayouttabspec)
-
-### `TabsLayoutTabSpec`
-
-The following table explains the usage of the tabs layout tab JSON fields:
-
-
-
-| Name | Usage |
-| ---- | ----- |
-| title? | The title of the tab. |
-| layout | Supported layouts are:
[GridLayoutKind](#gridlayoutkind)
[RowsLayoutKind](#rowslayoutkind)
[AutoGridLayoutKind](#autogridlayoutkind)
[TabsLayoutKind](#tabslayoutkind)
|
-| conditionalRendering? | `ConditionalRenderingGroupKind`. Rules for hiding or showing panels, if any. Consists of:
|
-
-
diff --git a/docs/sources/as-code/observability-as-code/schema-v2/librarypanel-schema.md b/docs/sources/as-code/observability-as-code/schema-v2/librarypanel-schema.md
deleted file mode 100644
index 45715e15b15..00000000000
--- a/docs/sources/as-code/observability-as-code/schema-v2/librarypanel-schema.md
+++ /dev/null
@@ -1,68 +0,0 @@
----
-description: A reference for the JSON library panel schema used with Observability as Code.
-keywords:
- - configuration
- - as code
- - as-code
- - dashboards
- - git integration
- - git sync
- - github
- - library panel
-labels:
- products:
- - cloud
- - enterprise
- - oss
-menuTitle: LibraryPanelKind schema
-title: LibraryPanelKind
-weight: 300
-canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/librarypanel-schema/
-aliases:
- - ../../../observability-as-code/schema-v2/librarypanel-schema/ # /docs/grafana/next/observability-as-code/schema-v2/librarypanel-schema/
----
-
-# `LibraryPanelKind`
-
-A library panel is a reusable panel that you can use in any dashboard.
-When you make a change to a library panel, that change propagates to all instances of where the panel is used.
-Library panels streamline reuse of panels across multiple dashboards.
-
-Following is the default library panel element JSON:
-
-```json
- "kind": "LibraryPanel",
- "spec": {
- "id": 0,
- "libraryPanel": {
- name: "",
- uid: "",
- }
- "title": ""
- }
-```
-
-The `LibraryPanelKind` consists of:
-
-- kind: "LibraryPanel"
-- spec: [LibraryPanelKindSpec](#librarypanelkindspec)
- - libraryPanel: [LibraryPanelRef](#librarypanelref)
-
-## `LibraryPanelKindSpec`
-
-The following table explains the usage of the library panel element JSON fields:
-
-| Name | Usage |
-| ------------ | ------------------------------------------------ |
-| id | Panel ID for the library panel in the dashboard. |
-| libraryPanel | [`LibraryPanelRef`](#librarypanelref) |
-| title | Title for the library panel in the dashboard. |
-
-### `LibraryPanelRef`
-
-The following table explains the usage of the library panel reference JSON fields:
-
-| Name | Usage |
-| ---- | ------------------ |
-| name | Library panel name |
-| uid | Library panel uid |
diff --git a/docs/sources/as-code/observability-as-code/schema-v2/links-schema.md b/docs/sources/as-code/observability-as-code/schema-v2/links-schema.md
deleted file mode 100644
index 0ddc50376de..00000000000
--- a/docs/sources/as-code/observability-as-code/schema-v2/links-schema.md
+++ /dev/null
@@ -1,67 +0,0 @@
----
-description: A reference for the JSON links schema used with Observability as Code.
-keywords:
- - configuration
- - as code
- - as-code
- - dashboards
- - git integration
- - git sync
- - github
- - links
-labels:
- products:
- - cloud
- - enterprise
- - oss
-menuTitle: links schema
-title: links
-weight: 500
-canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/links-schema/
-aliases:
- - ../../../observability-as-code/schema-v2/links-schema/ # /docs/grafana/next/observability-as-code/schema-v2/links-schema/
----
-
-# `links`
-
-The `links` schema is the configuration for links with references to other dashboards or external websites.
-Following are the default JSON fields:
-
-```json
- "links": [
- {
- "asDropdown": false,
- "icon": "",
- "includeVars": false,
- "keepTime": false,
- "tags": [],
- "targetBlank": false,
- "title": "",
- "tooltip": "",
- "type": "link",
- },
- ],
-```
-
-## `DashboardLink`
-
-The following table explains the usage of the dashboard link JSON fields.
-The table includes default and other fields:
-
-
-
-| Name | Usage |
-| ----------- | --------------------------------------- |
-| title | string. Title to display with the link. |
-| type | `DashboardLinkType`. Link type. Accepted values are:
dashboards - To refer to another dashboard
link - To refer to an external resource
|
-| icon | string. Icon name to be displayed with the link. |
-| tooltip | string. Tooltip to display when the user hovers their mouse over it. |
-| url? | string. Link URL. Only required/valid if the type is link. |
-| tags | string. List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards. |
-| asDropdown | bool. If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards. Default is `false`. |
-| targetBlank | bool. If true, the link will be opened in a new tab. Default is `false`. |
-| includeVars | bool. If true, includes current template variables values in the link as query params. Default is `false`. |
-| keepTime | bool. If true, includes current time range in the link as query params. Default is `false`. |
-| placement? | string. Use placement to display the link somewhere else on the dashboard other than above the visualizations. Use the `inControlsMenu` parameter to render the link in the dashboard controls dropdown menu. |
-
-
diff --git a/docs/sources/as-code/observability-as-code/schema-v2/panel-schema.md b/docs/sources/as-code/observability-as-code/schema-v2/panel-schema.md
deleted file mode 100644
index 088ab8eebf4..00000000000
--- a/docs/sources/as-code/observability-as-code/schema-v2/panel-schema.md
+++ /dev/null
@@ -1,305 +0,0 @@
----
-description: A reference for the JSON panel schema used with Observability as Code.
-keywords:
- - configuration
- - as code
- - as-code
- - dashboards
- - git integration
- - git sync
- - github
- - panels
-labels:
- products:
- - cloud
- - enterprise
- - oss
-menuTitle: PanelKind schema
-title: PanelKind
-weight: 200
-canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/panel-schema/
-aliases:
- - ../../../observability-as-code/schema-v2/panel-schema/ # /docs/grafana/next/observability-as-code/schema-v2/panel-schema/
----
-
-# `PanelKind`
-
-The panel element contains all the information about the panel including the visualization type, panel and visualization configuration, queries, and transformations.
-There's a panel element for each panel contained in the dashboard.
-
-Following is the default panel element JSON:
-
-```json
- "kind": "Panel",
- "spec": {
- "data": {
- "kind": "QueryGroup",
- "spec": {...},
- "description": "",
- "id": 0,
- "links": [],
- "title": "",
- "vizConfig": {
- "kind": "",
- "spec": {...},
- }
- }
-```
-
-The `PanelKind` consists of:
-
-- kind: "Panel"
-- spec: [PanelSpec](#panelspec)
-
-## `PanelSpec`
-
-The following table explains the usage of the panel element JSON fields:
-
-
-
-| Name | Usage |
-| ------------ | --------------------------------------------------------------------- |
-| data | `QueryGroupKind`, which includes queries and transformations. Consists of:
kind: "QueryGroup"
spec: [QueryGroupSpec](#querygroupspec)
|
-| description | The panel description. |
-| id | The panel ID. |
-| links | Links with references to other dashboards or external websites. |
-| title | The panel title. |
-| vizConfig | `VizConfigKind`. Includes visualization type, field configuration options, and all other visualization options. Consists of:
kind: string. Plugin ID.
spec: [VizConfigSpec](#vizconfigspec)
|
-| transparent? | bool. Controls whether or not the panel background is transparent. |
-
-
-
-### `QueryGroupSpec`
-
-
-
-| Name | Usage |
-| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| queries | `PanelQueryKind`. Consists of:
|
-| queryOptions | [`QueryOptionsSpec`](#queryoptionsspec) |
-
-
-
-#### `PanelQuerySpec`
-
-| Name | Usage |
-| ----------- | --------------------------------- |
-| query | [`DataQueryKind`](#dataquerykind) |
-| datasource? | [`DataSourceRef`](#datasourceref) |
-
-##### `DataQueryKind`
-
-| Name | Type |
-| ---- | ------ |
-| kind | string |
-| spec | string |
-
-##### `DataSourceRef`
-
-| Name | Usage |
-| ----- | ---------------------------------- |
-| type? | string. The plugin type-id. |
-| uid? | The specific data source instance. |
-
-#### `DataTransformerConfig`
-
-Transformations allow you to manipulate data returned by a query before the system applies a visualization.
-Using transformations you can: rename fields, join time series data, perform mathematical operations across queries, or use the output of one transformation as the input to another transformation.
-
-
-
-| Name | Usage |
-| --------- | ------------------------------------------- |
-| id | string. Unique identifier of transformer. |
-| disabled? | bool. Disabled transformations are skipped. |
-| filter? | [`MatcherConfig`](#matcherconfig). Optional frame matcher. When missing it will be applied to all results. |
-| topic? | `DataTopic`. Where to pull `DataFrames` from as input to transformation. Options are: `series`, `annotations`, and `alertStates`. |
-| options | Options to be passed to the transformer. Valid options depend on the transformer id. |
-
-
-
-##### `MatcherConfig`
-
-Matcher is a predicate configuration.
-Based on the configuration a set of field or values, it's filtered to apply an override or transformation.
-It comes with in id (to resolve implementation from registry) and a configuration that’s specific to a particular matcher type.
-
-| Name | Usage |
-| -------- | -------------------------------------------------------------------------------------- |
-| id | string. The matcher id. This is used to find the matcher implementation from registry. |
-| options? | The matcher options. This is specific to the matcher implementation. |
-
-#### `QueryOptionsSpec`
-
-| Name | Type |
-| ----------------- | ------- |
-| timeFrom? | string |
-| maxDataPoints? | integer |
-| timeShift? | string |
-| queryCachingTTL? | integer |
-| interval? | string |
-| cacheTimeout? | string |
-| hideTimeOverride? | bool |
-
-### `VizConfigSpec`
-
-| Name | Type/Definition |
-| ------------- | --------------------------------------- |
-| pluginVersion | string |
-| options | string |
-| fieldConfig | [FieldConfigSource](#fieldconfigsource) |
-
-#### `FieldConfigSource`
-
-The data model used in Grafana, namely the _data frame_, is a columnar-oriented table structure that unifies both time series and table query results.
-Each column within this structure is called a field.
-A field can represent a single time series or table column.
-Field options allow you to change how the data is displayed in your visualizations.
-
-
-
-| Name | Type/Definition |
-| ---------- | ------------------------------------- |
-| defaults | [`FieldConfig`](#fieldconfig). Defaults are the options applied to all fields. |
-| overrides | The options applied to specific fields overriding the defaults. |
-| matcher | [`MatcherConfig`](#matcherconfig). Optional frame matcher. When missing it will be applied to all results. |
-| properties | `DynamicConfigValue`. Consists of:
`id` - string
value?
|
-
-
-
-##### `FieldConfig`
-
-
-
-| Name | Type/Definition |
-| ------------------ | --------------------------------------- |
-| displayName? | string. The display value for this field. This supports template variables where empty is auto. |
-| displayNameFromDS? | string. This can be used by data sources that return an explicit naming structure for values and labels. When this property is configured, this value is used rather than the default naming strategy. |
-| description? | string. Human readable field metadata. |
-| path? | string. An explicit path to the field in the data source. When the frame meta includes a path, this will default to `${frame.meta.path}/${field.name}`. When defined, this value can be used as an identifier within the data source scope, and may be used to update the results. |
-| writeable? | bool. True if the data source can write a value to the path. Auth/authz are supported separately. |
-| filterable? | bool. True if the data source field supports ad-hoc filters. |
-| unit? | string. Unit a field should use. The unit you select is applied to all fields except time. You can use the unit's ID available in Grafana or a custom unit. [Available units in Grafana](https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts). As custom units, you can use the following formats:
`suffix:` for custom unit that should go after value.
`prefix:` for custom unit that should go before value.
`time:` for custom date time formats type for example
`time:YYYY-MM-DD`
`si:` for custom SI units. For example: `si: mF`. You can specify both a unit and the source data scale, so if your source data is represented as milli (thousands of) something, prefix the unit with that SI scale character.
`count:` for a custom count unit.
`currency:` for custom a currency unit.
|
-| decimals? | number. Specify the number of decimals Grafana includes in the rendered value. If you leave this field blank, Grafana automatically truncates the number of decimals based on the value. For example 1.1234 will display as 1.12 and 100.456 will display as 100. To display all decimals, set the unit to `string`. |
-| min? | number. The minimum value used in percentage threshold calculations. Leave empty for auto calculation based on all series and fields. |
-| max? | number. The maximum value used in percentage threshold calculations. Leave empty for auto calculation based on all series and fields. |
-| mappings? | `[...ValueMapping]`. Convert input values into a display string. Options are: [`ValueMap`](#valuemap), [`RangeMap`](#rangemap), [`RegexMap`](#rangemap), [`SpecialValueMap`](#specialvaluemap). |
-| thresholds? | `ThresholdsConfig`. Map numeric values to states. Consists of:
`mode` - `ThresholdsMode`. Options are: `absolute` and `percentage`.
`steps` - `[...Threshold]`
|
-| color? | [`FieldColor`](#fieldcolor). Panel color configuration. |
-| links? | `[...]`. The behavior when clicking a result. |
-| noValue? | string. Alternative to an empty string. |
-| custom? | `{...}`. Specified by the `FieldConfig` field in panel plugin schemas. |
-
-
-
-###### `ValueMap`
-
-Maps text values to a color or different display text and color.
-For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.
-
-
-
-| Name | Usage |
-| ------- | -------- |
-| type | `MappingType` & "value". `MappingType` options are: `value`, `range`, `regex`, and `special`. |
-| options | string. [`ValueMappingResult`](#valuemappingresult). Map with ``: `ValueMappingResult`. For example: `{ "10": { text: "Perfection!", color: "green" } }`. |
-
-
-
-###### `RangeMap`
-
-Maps numerical ranges to a display text and color.
-For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.
-
-
-
-| Name | Usage |
-| ------- | ---------------------------------------------------------------------------------------------------- |
-| type | `MappingType` & "range". `MappingType` options are: `value`, `range`, `regex`, and `special`. |
-| options | Range to match against and the result to apply when the value is within the range. Spec:
`from` - `float64` or `null`. Min value of the range. It can be null which means `-Infinity`.
`to` - `float64` or `null`. Max value of the range. It can be null which means `+Infinity`.
`result` - [`ValueMappingResult`](#valuemappingresult) |
-
-
-
-###### `RegexMap`
-
-Maps regular expressions to replacement text and a color.
-For example, if a value is `www.example.com`, you can configure a regex value mapping so that Grafana displays www and truncates the domain.
-
-
-
-| Name | Usage |
-| ------- | --------------------------------------------------------------------------------------------- |
-| type | `MappingType` & "regex". `MappingType` options are: `value`, `range`, `regex`, and `special`. |
-| options | Regular expression to match against and the result to apply when the value matches the regex. Spec:
`pattern` - string. Regular expression to match against.
`result` - [`ValueMappingResult`](#valuemappingresult) |
-
-
-
-###### `SpecialValueMap`
-
-Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color.
-See `SpecialValueMatch` in the following table to see the list of special values.
-For example, you can configure a special value mapping so that null values appear as N/A.
-
-
-
-| Name | Usage |
-| ------- | ----------------------------------------------------------------------------------------------- |
-| type | `MappingType` & "special". `MappingType` options are: `value`, `range`, `regex`, and `special`. |
-| options | Spec:
`match` - `SpecialValueMatch`. Special value to match against. Types are:
true
false
null
nan
empty
`result` - [`ValueMappingResult`](#valuemappingresult) |
-
-
-
-###### `ValueMappingResult`
-
-Result used as replacement with text and color when the value matches.
-
-| Name | Usage |
-| ----- | ----------------------------------------------------------------------------- |
-| text | string. Text to display when the value matches. |
-| color | string. Color to use when the value matches. |
-| icon | string. Icon to display when the value matches. Only specific visualizations. |
-| index | int32. Position in the mapping array. Only used internally. |
-
-###### `FieldColor`
-
-Map a field to a color.
-
-
-
-| Name | Usage |
-| ----------- | -------------------------------------------------------------------- |
-| mode | [`FieldColorModeId`](#fieldcolormodeid). The main color scheme mode. |
-| FixedColor? | string. The fixed color value for fixed or shades color modes. |
-| seriesBy? | `FieldColorSeriesByMode`. Some visualizations need to know how to assign a series color from by value color schemes. Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. Options are: `min`, `max`, and `last`. |
-
-
-
-###### `FieldColorModeId`
-
-Color mode for a field.
-You can specify a single color, or select a continuous (gradient) color schemes, based on a value.
-Continuous color interpolates a color using the percentage of a value relative to min and max.
-Accepted values are:
-
-
-
-| Name | Description |
-| --- | ---- |
-| thresholds | From thresholds. Informs Grafana to take the color from the matching threshold. |
-| palette-classic | Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for graphs and pie charts and other categorical data visualizations. |
-| palette-classic-by-name | Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations |
-| continuous-GrYlRd | Continuous Green-Yellow-Red palette mode |
-| continuous-RdYlGr | Continuous Red-Yellow-Green palette mode |
-| continuous-BlYlRd | Continuous Blue-Yellow-Red palette mode |
-| continuous-YlRd | Continuous Yellow-Red palette mode |
-| continuous-BlPu | Continuous Blue-Purple palette mode |
-| continuous-YlBl | Continuous Yellow-Blue palette mode |
-| continuous-blues | Continuous Blue palette mode |
-| continuous-reds | Continuous Red palette mode |
-| continuous-greens | Continuous Green palette mode |
-| continuous-purples | Continuous Purple palette mode |
-| shades | Shades of a single color. Specify a single color, useful in an override rule. |
-| fixed | Fixed color mode. Specify a single color, useful in an override rule. |
-
-
diff --git a/docs/sources/as-code/observability-as-code/schema-v2/timesettings-schema.md b/docs/sources/as-code/observability-as-code/schema-v2/timesettings-schema.md
deleted file mode 100644
index 8db14212740..00000000000
--- a/docs/sources/as-code/observability-as-code/schema-v2/timesettings-schema.md
+++ /dev/null
@@ -1,87 +0,0 @@
----
-description: A reference for the JSON timesettings schema used with Observability as Code.
-keywords:
- - configuration
- - as code
- - as-code
- - dashboards
- - git integration
- - git sync
- - github
- - time settings
-labels:
- products:
- - cloud
- - enterprise
- - oss
-menuTitle: timesettings schema
-title: timesettings
-weight: 600
-canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/timesettings-schema/
-aliases:
- - ../../../observability-as-code/schema-v2/timesettings-schema/ # /docs/grafana/next/observability-as-code/schema-v2/timesettings-schema/
----
-
-# `timeSettings`
-
-The `TimeSettingsSpec` defines the default time configuration for the time picker and the refresh picker for the specific dashboard.
-
-Following is the JSON for default time settings:
-
-```json
- "timeSettings": {
- "autoRefresh": "",
- "autoRefreshIntervals": [
- "5s",
- "10s",
- "30s",
- "1m",
- "5m",
- "15m",
- "30m",
- "1h",
- "2h",
- "1d"
- ],
- "fiscalYearStartMonth": 0,
- "from": "now-6h",
- "hideTimepicker": false,
- "timezone": "browser",
- "to": "now"
- },
-```
-
-`timeSettings` consists of:
-
-- [TimeSettingsSpec](#timesettingsspec)
-
-## `TimeSettingsSpec`
-
-The following table explains the usage of the time settings JSON fields:
-
-
-
-| Name | Usage |
-| ---- | ----- |
-| timezone? | string. Timezone of dashboard. Accepted values are IANA TZDB zone ID, `browser`, or `utc`. Default is `browser`. |
-| from | string. Start time range for dashboard. Accepted values are relative time strings like `now-6h` or absolute time strings like `2020-07-10T08:00:00.000Z`. Default is `now-6h`. |
-| to | string. End time range for dashboard. Accepted values are relative time strings like `now-6h` or absolute time strings like `2020-07-10T08:00:00.000Z`. Default is `now`. |
-| autoRefresh | string. Refresh rate of dashboard. Represented by interval string. For example: `5s`, `1m`, `1h`, `1d`. No default. In schema v1: `refresh`. |
-| autoRefreshIntervals | string. Interval options available in the refresh picker drop-down menu. The default array is `["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]`. |
-|quickRanges? | Selectable options available in the time picker drop-down menu. Has no effect on provisioned dashboard. Defined in the [`TimeRangeOption`](#timerangeoption) spec. In schema v1: `timepicker.quick_ranges`, not exposed in the UI. |
-| hideTimepicker | bool. Whether or not the time picker is visible. Default is `false`. In schema v1: `timepicker.hidden`. |
-| weekStart? | Day when the week starts. Expressed by the name of the day in lowercase. For example: `monday`. Options are `saturday`, `monday`, and `sunday`. |
-| fiscalYearStartMonth | The month that the fiscal year starts on. `0` = January, `11` = December |
-| nowDelay? | string. Override the "now" time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values. In schema v1: `timepicker.nowDelay`. |
-
-
-
-### `TimeRangeOption`
-
-The following table explains the usage of the time range option JSON fields:
-
-| Name | Usage |
-| ------- | ---------------------------------- |
-| display | string. Default is `Last 6 hours`. |
-| from | string. Default is `now-6h`. |
-| to | string. Default is `now`. |
diff --git a/docs/sources/as-code/observability-as-code/schema-v2/variables-schema.md b/docs/sources/as-code/observability-as-code/schema-v2/variables-schema.md
deleted file mode 100644
index 549478692f1..00000000000
--- a/docs/sources/as-code/observability-as-code/schema-v2/variables-schema.md
+++ /dev/null
@@ -1,501 +0,0 @@
----
-description: A reference for the JSON variables schema used with Observability as Code.
-keywords:
- - configuration
- - as code
- - as-code
- - dashboards
- - git integration
- - git sync
- - github
- - variables
-labels:
- products:
- - cloud
- - enterprise
- - oss
-menuTitle: variables schema
-title: variables
-weight: 700
-canonical: https://grafana.com/docs/grafana/latest/as-code/observability-as-code/schema-v2/variables-schema/
-aliases:
- - ../../../observability-as-code/schema-v2/variables-schema/ # /docs/grafana/next/observability-as-code/schema-v2/variables-schema/
----
-
-# `variables`
-
-The available variable types described in the following sections:
-
-- [QueryVariableKind](#queryvariablekind)
-- [TextVariableKind](#textvariablekind)
-- [ConstantVariableKind](#constantvariablekind)
-- [DatasourceVariableKind](#datasourcevariablekind)
-- [IntervalVariableKind](#intervalvariablekind)
-- [CustomVariableKind](#customvariablekind)
-- [SwitchVariableKind](#switchvariablekind)
-- [GroupByVariableKind](#groupbyvariablekind)
-- [AdhocVariableKind](#adhocvariablekind)
-
-## `QueryVariableKind`
-
-Following is the JSON for a default query variable:
-
-```json
- "variables": [
- {
- "kind": "QueryVariable",
- "spec": {
- "current": {
- "text": "",
- "value": ""
- },
- "hide": "dontHide",
- "includeAll": false,
- "multi": false,
- "name": "",
- "options": [],
- "query": defaultDataQueryKind(),
- "refresh": "never",
- "regex": "",
- "skipUrlSync": false,
- "sort": "disabled"
- }
- }
- ]
-```
-
-`QueryVariableKind` consists of:
-
-- kind: "QueryVariable"
-- spec: [QueryVariableSpec](#queryvariablespec)
-
-### `QueryVariableSpec`
-
-The following table explains the usage of the query variable JSON fields:
-
-
-
-| Name | Usage |
-| ------------ | ---------------------------------------------- |
-| name | string. Name of the variable. |
-| current | "Text" and a "value" or [`VariableOption`](#variableoption) |
-| label? | string |
-| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
-| refresh | `VariableRefresh`. Options are `never`, `onDashboardLoad`, and `onTimeChanged`. |
-| skipUrlSync | bool. Default is `false`. |
-| description? | string |
-| datasource? | [`DataSourceRef`](#datasourceref) |
-| query | `DataQueryKind`. Consists of:
|
-| definition? | string |
-| options | [`VariableOption`](#variableoption) |
-| multi | bool. Default is `false`. |
-| includeAll | bool. Default is `false`. |
-| allValue? | string |
-| placeholder? | string |
-
-
-
-#### `VariableOption`
-
-| Name | Usage |
-| -------- | -------------------------------------------- |
-| selected | bool. Whether or not the option is selected. |
-| text | string. Text to be displayed for the option. |
-| value | string. Value of the option. |
-
-#### `DataSourceRef`
-
-| Name | Usage |
-| ----- | ---------------------------------- |
-| type? | string. The plugin type-id. |
-| uid? | The specific data source instance. |
-
-## `TextVariableKind`
-
-Following is the JSON for a default text variable:
-
-```json
- "variables": [
- {
- "kind": "TextVariable",
- "spec": {
- "current": {
- "text": "",
- "value": ""
- },
- "hide": "dontHide",
- "name": "",
- "query": "",
- "skipUrlSync": false
- }
- }
- ]
-```
-
-`TextVariableKind` consists of:
-
-- kind: TextVariableKind
-- spec: [TextVariableSpec](#textvariablespec)
-
-### `TextVariableSpec`
-
-The following table explains the usage of the query variable JSON fields:
-
-| Name | Usage |
-| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
-| name | string. Name of the variable. |
-| current | "Text" and a "value" or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
-| query | string |
-| label? | string |
-| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
-| skipUrlSync | bool. Default is `false`. |
-| description? | string |
-
-## `ConstantVariableKind`
-
-Following is the JSON for a default constant variable:
-
-```json
- "variables": [
- {
- "kind": "ConstantVariable",
- "spec": {
- "current": {
- "text": "",
- "value": ""
- },
- "hide": "hideVariable",
- "name": "",
- "query": "",
- "skipUrlSync": true
- }
- }
- ]
-```
-
-`ConstantVariableKind` consists of:
-
-- kind: "ConstantVariable"
-- spec: [ConstantVariableSpec](#constantvariablespec)
-
-### `ConstantVariableSpec`
-
-The following table explains the usage of the constant variable JSON fields:
-
-| Name | Usage |
-| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
-| name | string. Name of the variable. |
-| query | string |
-| current | "Text" and a "value" or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
-| label? | string |
-| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
-| skipUrlSync | bool. Default is `false`. |
-| description? | string |
-
-## `DatasourceVariableKind`
-
-Following is the JSON for a default data source variable:
-
-```json
- "variables": [
- {
- "kind": "DatasourceVariable",
- "spec": {
- "current": {
- "text": "",
- "value": ""
- },
- "hide": "dontHide",
- "includeAll": false,
- "multi": false,
- "name": "",
- "options": [],
- "pluginId": "",
- "refresh": "never",
- "regex": "",
- "skipUrlSync": false
- }
- }
- ]
-```
-
-`DatasourceVariableKind` consists of:
-
-- kind: "DatasourceVariable"
-- spec: [DatasourceVariableSpec](#datasourcevariablespec)
-
-### `DatasourceVariableSpec`
-
-The following table explains the usage of the data source variable JSON fields:
-
-| Name | Usage |
-| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
-| name | string. Name of the variable. |
-| pluginId | string |
-| refresh | `VariableRefresh`. Options are `never`, `onDashboardLoad`, and `onTimeChanged`. |
-| regex | string |
-| current | `Text` and a `value` or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
-| options | `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
-| multi | bool. Default is `false`. |
-| includeAll | bool. Default is `false`. |
-| allValue? | string |
-| label? | string |
-| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
-| skipUrlSync | bool. Default is `false`. |
-| description? | string |
-
-## `IntervalVariableKind`
-
-Following is the JSON for a default interval variable:
-
-```json
- "variables": [
- {
- "kind": "IntervalVariable",
- "spec": {
- "auto": false,
- "auto_count": 0,
- "auto_min": "",
- "current": {
- "text": "",
- "value": ""
- },
- "hide": "dontHide",
- "name": "",
- "options": [],
- "query": "",
- "refresh": "never",
- "skipUrlSync": false
- }
- }
- ]
-```
-
-`IntervalVariableKind` consists of:
-
-- kind: "IntervalVariable"
-- spec: [IntervalVariableSpec](#intervalvariablespec)
-
-### `IntervalVariableSpec`
-
-The following table explains the usage of the interval variable JSON fields:
-
-| Name | Usage |
-| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
-| name | string. Name of the variable. |
-| query | string |
-| current | `Text` and a `value` or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
-| options | `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
-| auto | bool. Default is `false`. |
-| auto_count | integer. Default is `0`. |
-| refresh | `VariableRefresh`. Options are `never`, `onDashboardLoad`, and `onTimeChanged`. |
-| label? | string |
-| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
-| skipUrlSync | bool. Default is `false` |
-| description? | string |
-
-## `CustomVariableKind`
-
-Following is the JSON for a default custom variable:
-
-```json
- "variables": [
- {
- "kind": "CustomVariable",
- "spec": {
- "current": defaultVariableOption(),
- "hide": "dontHide",
- "includeAll": false,
- "multi": false,
- "name": "",
- "options": [],
- "query": "",
- "skipUrlSync": false
- }
- }
- ]
-```
-
-`CustomVariableKind` consists of:
-
-- kind: "CustomVariable"
-- spec: [CustomVariableSpec](#customvariablespec)
-
-### `CustomVariableSpec`
-
-The following table explains the usage of the custom variable JSON fields:
-
-| Name | Usage |
-| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
-| name | string. Name of the variable. |
-| query | string |
-| current | `Text` and a `value` or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
-| options | `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
-| multi | bool. Default is `false`. |
-| includeAll | bool. Default is `false`. |
-| allValue? | string |
-| label? | string |
-| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
-| skipUrlSync | bool. Default is `false`. |
-| description? | string |
-
-## `SwitchVariableKind`
-
-Following is the JSON for a default switch variable:
-
-```json
- "variables": [
- {
- "kind": "SwitchVariable",
- "spec": {
- "current": "false",
- "enabledValue": "true",
- "disabledValue": "false",
- "hide": "dontHide",
- "name": "",
- "skipUrlSync": false
- }
- }
- ]
-```
-
-`SwitchVariableKind` consists of:
-
-- kind: "SwitchVariable"
-- spec: [SwitchVariableSpec](#switchvariablespec)
-
-### `SwitchVariableSpec`
-
-The following table explains the usage of the switch variable JSON fields:
-
-
-
-| Name | Usage |
-| -------------- | -------------------------------------------------------------------------------------------------------------------------------- |
-| name | string. Name of the variable. |
-| current | string. Current value of the switch variable (either `enabledValue` or `disabledValue`). |
-| enabledValue | string. Value when the switch is in the enabled state. |
-| disabledValue | string. Value when the switch is in the disabled state. |
-| label? | string |
-| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
-| skipUrlSync | bool. Default is `false`. |
-| description? | string |
-
-
-
-## `GroupByVariableKind`
-
-Following is the JSON for a default group by variable:
-
-```json
- "variables": [
- {
- "kind": "GroupByVariable",
- "spec": {
- "current": {
- "text": [
- ""
- ],
- "value": [
- ""
- ]
- },
- "datasource": {},
- "hide": "dontHide",
- "multi": false,
- "name": "",
- "options": [],
- "skipUrlSync": false
- }
- }
- ]
-```
-
-`GroupByVariableKind` consists of:
-
-- kind: "GroupByVariable"
-- spec: [GroupByVariableSpec](#groupbyvariablespec)
-
-### `GroupByVariableSpec`
-
-The following table explains the usage of the group by variable JSON fields:
-
-| Name | Usage |
-| ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
-| name | string. Name of the variable |
-| datasource? | `DataSourceRef`. Refer to the [`DataSourceRef` definition](#datasourceref) under `QueryVariableKind`. |
-| current | `Text` and a `value` or `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
-| options | `VariableOption`. Refer to the [`VariableOption` definition](#variableoption) under `QueryVariableKind`. |
-| multi | bool. Default is `false`. |
-| label? | string |
-| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
-| skipUrlSync | bool. Default is `false`. |
-| description? | string. |
-
-## `AdhocVariableKind`
-
-Following is the JSON for a default ad hoc variable:
-
-```json
- "variables": [
- {
- "kind": "AdhocVariable",
- "spec": {
- "baseFilters": [],
- "defaultKeys": [],
- "filters": [],
- "hide": "dontHide",
- "name": "",
- "skipUrlSync": false
- }
- }
- ]
-```
-
-`AdhocVariableKind` consists of:
-
-- kind: "AdhocVariable"
-- spec: [AdhocVariableSpec](#adhocvariablespec)
-
-### `AdhocVariableSpec`
-
-The following table explains the usage of the ad hoc variable JSON fields:
-
-| Name | Usage |
-| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
-| name | string. Name of the variable. |
-| datasource? | `DataSourceRef`. Consists of:
type? - string. The plugin type-id.
uid? - string. The specific data source instance.
|
-| baseFilters | [AdHocFilterWithLabels](#adhocfilterswithlabels) |
-| filters | [AdHocFilterWithLabels](#adhocfilterswithlabels) |
-| defaultKeys | [MetricFindValue](#metricfindvalue) |
-| label? | string |
-| hide | `VariableHide`. Options are: `dontHide`, `hideLabel`, and `hideVariable`. |
-| skipUrlSync | bool. Default is `false`. |
-| description? | string |
-
-#### `AdHocFiltersWithLabels`
-
-The following table explains the usage of the ad hoc variable with labels JSON fields:
-
-| Name | Type |
-| ------------ | ------------- |
-| key | string |
-| operator | string |
-| value | string |
-| values? | `[...string]` |
-| keyLabel | string |
-| valueLabels? | `[...string]` |
-| forceEdit? | bool |
-
-#### `MetricFindValue`
-
-The following table explains the usage of the metric find value JSON fields:
-
-| Name | Type |
-| ----------- | ---------------- |
-| text | string |
-| value? | string or number |
-| group? | string |
-| expandable? | bool |
diff --git a/docs/sources/datasources/google-cloud-monitoring/_index.md b/docs/sources/datasources/google-cloud-monitoring/_index.md
index 27bc7b390cd..d5412c33ef7 100644
--- a/docs/sources/datasources/google-cloud-monitoring/_index.md
+++ b/docs/sources/datasources/google-cloud-monitoring/_index.md
@@ -103,10 +103,11 @@ To configure basic settings for the data source, complete the following steps:
1. Set the data source's basic configuration options:
- | Name | Description |
- | ----------- | ------------------------------------------------------------------------ |
- | **Name** | Sets the name you use to refer to the data source in panels and queries. |
- | **Default** | Sets whether the data source is pre-selected for new panels. |
+ | Name | Description |
+ | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+ | **Name** | Sets the name you use to refer to the data source in panels and queries. |
+ | **Default** | Sets whether the data source is pre-selected for new panels. |
+ | **Universe Domain** | The universe domain to connect to. For more information, refer to [Documentation on universe domains](https://docs.cloud.google.com/python/docs/reference/monitoring/latest/google.cloud.monitoring_v3.services.service_monitoring_service.ServiceMonitoringServiceAsyncClient#google_cloud_monitoring_v3_services_service_monitoring_service_ServiceMonitoringServiceAsyncClient_universe_domain). Defaults to `googleapis.com`. |
### Provision the data source
@@ -129,6 +130,7 @@ datasources:
clientEmail: stackdriver@myproject.iam.gserviceaccount.com
authenticationType: jwt
defaultProject: my-project-name
+ universeDomain: googleapis.com
secureJsonData:
privateKey: |
-----BEGIN PRIVATE KEY-----
@@ -152,6 +154,7 @@ datasources:
clientEmail: stackdriver@myproject.iam.gserviceaccount.com
authenticationType: jwt
defaultProject: my-project-name
+ universeDomain: googleapis.com
privateKeyPath: /etc/secrets/gce.pem
```
@@ -166,6 +169,7 @@ datasources:
access: proxy
jsonData:
authenticationType: gce
+ universeDomain: googleapis.com
```
## Import pre-configured dashboards
diff --git a/docs/sources/developer-resources/api-reference/http-api/dashboard_versions.md b/docs/sources/developer-resources/api-reference/http-api/dashboard_versions.md
index f4b46adad3f..370b1fed7e4 100644
--- a/docs/sources/developer-resources/api-reference/http-api/dashboard_versions.md
+++ b/docs/sources/developer-resources/api-reference/http-api/dashboard_versions.md
@@ -171,146 +171,3 @@ Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **404** - Dashboard version not found
-
-## Restore dashboard by dashboard UID
-
-`POST /api/dashboards/uid/:uid/restore`
-
-Restores a dashboard to a given dashboard version using `uid`.
-
-**Example request for restoring a dashboard version**:
-
-```http
-POST /api/dashboards/uid/QA7wKklGz/restore
-Accept: application/json
-Content-Type: application/json
-Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
-
-{
- "version": 1
-}
-```
-
-JSON body schema:
-
-- **version** - The dashboard version to restore to
-
-**Example response**:
-
-```http
-HTTP/1.1 200 OK
-Content-Type: application/json; charset=UTF-8
-Content-Length: 67
-
-{
- "id": 70,
- "slug": "my-dashboard",
- "status": "success",
- "uid": "QA7wKklGz",
- "url": "/d/QA7wKklGz/my-dashboard",
- "version": 3
-}
-```
-
-JSON response body schema:
-
-- **slug** - the URL friendly slug of the dashboard's title
-- **status** - whether the restoration was successful or not
-- **version** - the new dashboard version, following the restoration
-
-Status codes:
-
-- **200** - OK
-- **400** - Bad request (specified version has the same content as the current dashboard)
-- **401** - Unauthorized
-- **404** - Not found (dashboard not found or dashboard version not found)
-- **500** - Internal server error (indicates issue retrieving dashboard tags from database)
-
-**Example error response**
-
-```http
-HTTP/1.1 404 Not Found
-Content-Type: application/json; charset=UTF-8
-Content-Length: 46
-
-{
- "message": "Dashboard version not found"
-}
-```
-
-JSON response body schema:
-
-- **message** - Message explaining the reason for the request failure.
-
-## Compare dashboard versions
-
-`POST /api/dashboards/calculate-diff`
-
-Compares two dashboard versions by calculating the JSON diff of them.
-
-**Example request**:
-
-```http
-POST /api/dashboards/calculate-diff HTTP/1.1
-Accept: text/html
-Content-Type: application/json
-Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
-
-{
- "base": {
- "dashboardId": 1,
- "version": 1
- },
- "new": {
- "dashboardId": 1,
- "version": 2
- },
- "diffType": "json"
-}
-```
-
-JSON body schema:
-
-- **base** - an object representing the base dashboard version
-- **new** - an object representing the new dashboard version
-- **diffType** - the type of diff to return. Can be "json" or "basic".
-
-**Example response (JSON diff)**:
-
-```http
-HTTP/1.1 200 OK
-Content-Type: text/html; charset=UTF-8
-
-
-
-
-```
-
-The response is a textual representation of the diff, with the dashboard values being in JSON, similar to the diffs seen on sites like GitHub or GitLab.
-
-Status Codes:
-
-- **200** - Ok
-- **400** - Bad request (invalid JSON sent)
-- **401** - Unauthorized
-- **404** - Not found
-
-**Example response (basic diff)**:
-
-```http
-HTTP/1.1 200 OK
-Content-Type: text/html; charset=UTF-8
-
-
- );
- expect(screen.getByText('Series 1')).toBeInTheDocument();
- });
-
- it('renders with long label text', () => {
- const longLabelItem: VizLegendItem = {
- ...mockItem,
- label: 'This is a very long series name that should be scrollable in the table cell',
- };
- render(
-
-
-
-
-
- );
- expect(
- screen.getByText('This is a very long series name that should be scrollable in the table cell')
- ).toBeInTheDocument();
- });
-
- it('renders stat values when provided', () => {
- const itemWithStats: VizLegendItem = {
- ...mockItem,
- getDisplayValues: () => [
- { numeric: 100, text: '100', title: 'Max' },
- { numeric: 50, text: '50', title: 'Min' },
- ],
- };
- render(
-
{item.getDisplayValues &&
@@ -130,28 +128,6 @@ const getStyles = (theme: GrafanaTheme2) => {
background: rowHoverBg,
},
}),
- labelCell: css({
- label: 'LegendLabelCell',
- maxWidth: 0,
- width: '100%',
- minWidth: theme.spacing(16),
- }),
- labelCellInner: css({
- label: 'LegendLabelCellInner',
- display: 'block',
- flex: 1,
- minWidth: 0,
- overflowX: 'auto',
- overflowY: 'hidden',
- paddingRight: theme.spacing(3),
- scrollbarWidth: 'none',
- msOverflowStyle: 'none',
- maskImage: `linear-gradient(to right, black calc(100% - ${theme.spacing(3)}), transparent 100%)`,
- WebkitMaskImage: `linear-gradient(to right, black calc(100% - ${theme.spacing(3)}), transparent 100%)`,
- '&::-webkit-scrollbar': {
- display: 'none',
- },
- }),
label: css({
label: 'LegendLabel',
whiteSpace: 'nowrap',
@@ -159,6 +135,9 @@ const getStyles = (theme: GrafanaTheme2) => {
border: 'none',
fontSize: 'inherit',
padding: 0,
+ maxWidth: '600px',
+ textOverflow: 'ellipsis',
+ overflow: 'hidden',
userSelect: 'text',
}),
labelDisabled: css({
diff --git a/packaging/docker/run.sh b/packaging/docker/run.sh
index a4c91b49379..148d7ccb30f 100755
--- a/packaging/docker/run.sh
+++ b/packaging/docker/run.sh
@@ -1,4 +1,5 @@
-#!/bin/bash -e
+#!/bin/bash
+set -e
PERMISSIONS_OK=0
@@ -26,14 +27,14 @@ if [ ! -d "$GF_PATHS_PLUGINS" ]; then
fi
if [ ! -z ${GF_AWS_PROFILES+x} ]; then
- > "$GF_PATHS_HOME/.aws/credentials"
+ :> "$GF_PATHS_HOME/.aws/credentials"
for profile in ${GF_AWS_PROFILES}; do
access_key_varname="GF_AWS_${profile}_ACCESS_KEY_ID"
secret_key_varname="GF_AWS_${profile}_SECRET_ACCESS_KEY"
region_varname="GF_AWS_${profile}_REGION"
- if [ ! -z "${!access_key_varname}" -a ! -z "${!secret_key_varname}" ]; then
+ if [ ! -z "${!access_key_varname}" ] && [ ! -z "${!secret_key_varname}" ]; then
echo "[${profile}]" >> "$GF_PATHS_HOME/.aws/credentials"
echo "aws_access_key_id = ${!access_key_varname}" >> "$GF_PATHS_HOME/.aws/credentials"
echo "aws_secret_access_key = ${!secret_key_varname}" >> "$GF_PATHS_HOME/.aws/credentials"
diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go
index a560ff47c5d..2e3b955a247 100644
--- a/pkg/api/dashboard.go
+++ b/pkg/api/dashboard.go
@@ -795,6 +795,10 @@ func (hs *HTTPServer) GetDashboardVersion(c *contextmodel.ReqContext) response.R
// swagger:route POST /dashboards/uid/{uid}/restore dashboards versions restoreDashboardVersionByUID
//
// Restore a dashboard to a given dashboard version using UID.
+// This API will be removed when /apis/dashboards.grafana.app/v1 is released.
+// You can restore a dashboard by reading it from history, then creating it again.
+//
+// Deprecated: true
//
// Responses:
// 200: postDashboardResponse
diff --git a/pkg/extensions/enterprise_imports.go b/pkg/extensions/enterprise_imports.go
index 472652cc103..3a65082459e 100644
--- a/pkg/extensions/enterprise_imports.go
+++ b/pkg/extensions/enterprise_imports.go
@@ -11,6 +11,7 @@ import (
_ "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
_ "github.com/Azure/go-autorest/autorest"
_ "github.com/Azure/go-autorest/autorest/adal"
+ _ "github.com/aws/aws-sdk-go-v2/service/secretsmanager"
_ "github.com/beevik/etree"
_ "github.com/blugelabs/bluge"
_ "github.com/blugelabs/bluge_segment_api"
diff --git a/pkg/registry/apis/iam/authorizer.go b/pkg/registry/apis/iam/authorizer.go
index efcf6fa5b39..da81dd2d9a8 100644
--- a/pkg/registry/apis/iam/authorizer.go
+++ b/pkg/registry/apis/iam/authorizer.go
@@ -42,7 +42,6 @@ func newIAMAuthorizer(
// Identity specific resources
legacyAuthorizer := gfauthorizer.NewResourceAuthorizer(legacyAccessClient)
- resourceAuthorizer[iamv0.TeamBindingResourceInfo.GetName()] = legacyAuthorizer
resourceAuthorizer["display"] = legacyAuthorizer
// Access specific resources
@@ -55,6 +54,7 @@ func newIAMAuthorizer(
resourceAuthorizer[iamv0.UserResourceInfo.GetName()] = authorizer
resourceAuthorizer[iamv0.ExternalGroupMappingResourceInfo.GetName()] = allowAuthorizer
resourceAuthorizer[iamv0.TeamResourceInfo.GetName()] = authorizer
+ resourceAuthorizer[iamv0.TeamBindingResourceInfo.GetName()] = allowAuthorizer
resourceAuthorizer["searchUsers"] = serviceAuthorizer
resourceAuthorizer["searchTeams"] = serviceAuthorizer
diff --git a/pkg/registry/apis/iam/authorizer/team_binding_authorizer.go b/pkg/registry/apis/iam/authorizer/team_binding_authorizer.go
new file mode 100644
index 00000000000..2a4f5ae5e51
--- /dev/null
+++ b/pkg/registry/apis/iam/authorizer/team_binding_authorizer.go
@@ -0,0 +1,156 @@
+package authorizer
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/grafana/authlib/types"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
+ "k8s.io/apimachinery/pkg/runtime"
+
+ iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
+ "github.com/grafana/grafana/pkg/apimachinery/utils"
+ "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer/storewrapper"
+)
+
+type TeamBindingAuthorizer struct {
+ accessClient types.AccessClient
+}
+
+var _ storewrapper.ResourceStorageAuthorizer = (*TeamBindingAuthorizer)(nil)
+
+func NewTeamBindingAuthorizer(
+ accessClient types.AccessClient,
+) *TeamBindingAuthorizer {
+ return &TeamBindingAuthorizer{
+ accessClient: accessClient,
+ }
+}
+
+// AfterGet implements ResourceStorageAuthorizer.
+func (r *TeamBindingAuthorizer) AfterGet(ctx context.Context, obj runtime.Object) error {
+ authInfo, ok := types.AuthInfoFrom(ctx)
+ if !ok {
+ return storewrapper.ErrUnauthenticated
+ }
+
+ concreteObj, ok := obj.(*iamv0.TeamBinding)
+ if !ok {
+ return apierrors.NewInternalError(fmt.Errorf("expected TeamBinding, got %T: %w", obj, storewrapper.ErrUnexpectedType))
+ }
+
+ // Accesscontrol should check on the TeamResourceInfo group resource if the user can use VerbGetPermissions
+ // on the team (TeamRef.Name) (handled below) OR if the subject's name (TeamBindingSpec.Subject.Name) is equal to the current Identity's UID/Identifier.
+ if concreteObj.Spec.Subject.Name == authInfo.GetIdentifier() {
+ return nil
+ }
+
+ teamName := concreteObj.Spec.TeamRef.Name
+ checkReq := types.CheckRequest{
+ Namespace: authInfo.GetNamespace(),
+ Group: iamv0.TeamResourceInfo.GroupResource().Group,
+ Resource: iamv0.TeamResourceInfo.GroupResource().Resource,
+ Verb: utils.VerbGetPermissions,
+ Name: teamName,
+ }
+ res, err := r.accessClient.Check(ctx, authInfo, checkReq, "")
+ if err != nil {
+ return apierrors.NewInternalError(err)
+ }
+
+ if !res.Allowed {
+ return apierrors.NewForbidden(
+ iamv0.TeamBindingResourceInfo.GroupResource(),
+ concreteObj.Name,
+ fmt.Errorf("user cannot access team %s", teamName),
+ )
+ }
+ return nil
+}
+
+// BeforeCreate implements ResourceStorageAuthorizer.
+func (r *TeamBindingAuthorizer) BeforeCreate(ctx context.Context, obj runtime.Object) error {
+ return r.beforeWrite(ctx, obj)
+}
+
+// BeforeDelete implements ResourceStorageAuthorizer.
+func (r *TeamBindingAuthorizer) BeforeDelete(ctx context.Context, obj runtime.Object) error {
+ return r.beforeWrite(ctx, obj)
+}
+
+// BeforeUpdate implements ResourceStorageAuthorizer.
+func (r *TeamBindingAuthorizer) BeforeUpdate(ctx context.Context, obj runtime.Object) error {
+ return r.beforeWrite(ctx, obj)
+}
+
+func (r *TeamBindingAuthorizer) beforeWrite(ctx context.Context, obj runtime.Object) error {
+ authInfo, ok := types.AuthInfoFrom(ctx)
+ if !ok {
+ return storewrapper.ErrUnauthenticated
+ }
+
+ concreteObj, ok := obj.(*iamv0.TeamBinding)
+ if !ok {
+ return apierrors.NewInternalError(fmt.Errorf("expected TeamBinding, got %T: %w", obj, storewrapper.ErrUnexpectedType))
+ }
+
+ teamName := concreteObj.Spec.TeamRef.Name
+ checkReq := types.CheckRequest{
+ Namespace: authInfo.GetNamespace(),
+ Group: iamv0.GROUP,
+ Resource: iamv0.TeamResourceInfo.GetName(),
+ Verb: utils.VerbSetPermissions,
+ Name: teamName,
+ }
+
+ res, err := r.accessClient.Check(ctx, authInfo, checkReq, "")
+ if err != nil {
+ return apierrors.NewInternalError(err)
+ }
+
+ if !res.Allowed {
+ return apierrors.NewForbidden(
+ iamv0.TeamBindingResourceInfo.GroupResource(),
+ concreteObj.Name,
+ fmt.Errorf("user cannot write team %s", teamName),
+ )
+ }
+ return nil
+}
+
+// FilterList implements ResourceStorageAuthorizer.
+func (r *TeamBindingAuthorizer) FilterList(ctx context.Context, list runtime.Object) (runtime.Object, error) {
+ authInfo, ok := types.AuthInfoFrom(ctx)
+ if !ok {
+ return nil, storewrapper.ErrUnauthenticated
+ }
+
+ l, ok := list.(*iamv0.TeamBindingList)
+ if !ok {
+ return nil, apierrors.NewInternalError(fmt.Errorf("expected TeamBindingList, got %T: %w", list, storewrapper.ErrUnexpectedType))
+ }
+
+ var filteredItems []iamv0.TeamBinding
+
+ listReq := types.ListRequest{
+ Namespace: authInfo.GetNamespace(),
+ Group: iamv0.TeamResourceInfo.GroupResource().Group,
+ Resource: iamv0.TeamResourceInfo.GroupResource().Resource,
+ Verb: utils.VerbGetPermissions,
+ }
+ canView, _, err := r.accessClient.Compile(ctx, authInfo, listReq)
+ if err != nil {
+ return nil, apierrors.NewInternalError(err)
+ }
+
+ for _, item := range l.Items {
+ // Accesscontrol should check on the TeamResourceInfo group resource if the user can use VerbGetPermissions
+ // on the team (TeamRef.Name) OR if the subject's name (TeamBindingSpec.Subject.Name) is equal to the current Identity's UID/Identifier.
+ if item.Spec.Subject.Name == authInfo.GetIdentifier() || canView(item.Spec.TeamRef.Name, "") {
+ filteredItems = append(filteredItems, item)
+ }
+ }
+
+ l.Items = filteredItems
+ return l, nil
+}
diff --git a/pkg/registry/apis/iam/authorizer/team_binding_authorizer_test.go b/pkg/registry/apis/iam/authorizer/team_binding_authorizer_test.go
new file mode 100644
index 00000000000..f3bf8795de1
--- /dev/null
+++ b/pkg/registry/apis/iam/authorizer/team_binding_authorizer_test.go
@@ -0,0 +1,253 @@
+package authorizer
+
+import (
+ "context"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ "github.com/grafana/authlib/types"
+ iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
+ "github.com/grafana/grafana/pkg/apimachinery/utils"
+)
+
+func newTeamBinding(teamName, name, subjectName string) *iamv0.TeamBinding {
+ return &iamv0.TeamBinding{
+ ObjectMeta: metav1.ObjectMeta{Namespace: "org-2", Name: name},
+ Spec: iamv0.TeamBindingSpec{
+ TeamRef: iamv0.TeamBindingTeamRef{
+ Name: teamName,
+ },
+ Subject: iamv0.TeamBindingspecSubject{
+ Name: subjectName,
+ },
+ },
+ }
+}
+
+func TestTeamBinding_AfterGet(t *testing.T) {
+ tests := []struct {
+ name string
+ teamBinding *iamv0.TeamBinding
+ shouldAllow bool
+ checkCalled bool
+ }{
+ {
+ name: "allow access via permission",
+ teamBinding: newTeamBinding("team-1", "binding-1", "other"),
+ shouldAllow: true,
+ checkCalled: true,
+ },
+ {
+ name: "deny access",
+ teamBinding: newTeamBinding("team-1", "binding-1", "other"),
+ shouldAllow: false,
+ checkCalled: true, // called but returns allowed=false
+ },
+ {
+ name: "allow access via subject match",
+ teamBinding: newTeamBinding("team-1", "binding-1", "u001"),
+ shouldAllow: true,
+ checkCalled: false, // short-circuits
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ checkFunc := func(id types.AuthInfo, req *types.CheckRequest, folder string) (types.CheckResponse, error) {
+ require.NotNil(t, id)
+ require.Equal(t, "u001", id.GetIdentifier())
+
+ require.Equal(t, "org-2", req.Namespace)
+ require.Equal(t, iamv0.GROUP, req.Group)
+ require.Equal(t, iamv0.TeamResourceInfo.GetName(), req.Resource)
+ require.Equal(t, "team-1", req.Name)
+ require.Equal(t, utils.VerbGetPermissions, req.Verb)
+
+ return types.CheckResponse{Allowed: tt.shouldAllow}, nil
+ }
+
+ accessClient := &fakeAccessClient{checkFunc: checkFunc}
+ authz := NewTeamBindingAuthorizer(accessClient)
+ ctx := types.WithAuthInfo(context.Background(), user)
+
+ err := authz.AfterGet(ctx, tt.teamBinding)
+ if tt.shouldAllow {
+ require.NoError(t, err)
+ } else {
+ require.Error(t, err)
+ }
+ require.Equal(t, tt.checkCalled, accessClient.checkCalled)
+ })
+ }
+}
+
+func TestTeamBinding_FilterList(t *testing.T) {
+ list := &iamv0.TeamBindingList{
+ Items: []iamv0.TeamBinding{
+ *newTeamBinding("team-1", "binding-1", "other"), // Access via permission
+ *newTeamBinding("team-2", "binding-2", "other"), // No access
+ *newTeamBinding("team-3", "binding-3", "u001"), // Access via subject match
+ },
+ }
+
+ compileFunc := func(id types.AuthInfo, req types.ListRequest) (types.ItemChecker, types.Zookie, error) {
+ require.NotNil(t, id)
+ require.Equal(t, "u001", id.GetIdentifier())
+
+ require.Equal(t, "org-2", req.Namespace)
+ require.Equal(t, iamv0.GROUP, req.Group)
+ require.Equal(t, iamv0.TeamResourceInfo.GroupResource().Resource, req.Resource)
+ require.Equal(t, utils.VerbGetPermissions, req.Verb)
+
+ return func(name, folder string) bool {
+ return name == "team-1"
+ }, &types.NoopZookie{}, nil
+ }
+
+ accessClient := &fakeAccessClient{compileFunc: compileFunc}
+ authz := NewTeamBindingAuthorizer(accessClient)
+ ctx := types.WithAuthInfo(context.Background(), user)
+
+ obj, err := authz.FilterList(ctx, list)
+ require.NoError(t, err)
+ require.NotNil(t, list)
+ require.True(t, accessClient.compileCalled)
+
+ filtered, ok := obj.(*iamv0.TeamBindingList)
+ require.True(t, ok)
+ require.Len(t, filtered.Items, 2)
+
+ names := []string{filtered.Items[0].Name, filtered.Items[1].Name}
+ require.Contains(t, names, "binding-1")
+ require.Contains(t, names, "binding-3")
+}
+
+func TestTeamBinding_BeforeCreate(t *testing.T) {
+ binding := newTeamBinding("team-1", "binding-1", "other")
+
+ tests := []struct {
+ name string
+ shouldAllow bool
+ }{
+ {
+ name: "allow create",
+ shouldAllow: true,
+ },
+ {
+ name: "deny create",
+ shouldAllow: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ checkFunc := func(id types.AuthInfo, req *types.CheckRequest, folder string) (types.CheckResponse, error) {
+ require.Equal(t, "org-2", req.Namespace)
+ require.Equal(t, iamv0.GROUP, req.Group)
+ require.Equal(t, iamv0.TeamResourceInfo.GetName(), req.Resource)
+ require.Equal(t, "team-1", req.Name)
+ require.Equal(t, utils.VerbSetPermissions, req.Verb)
+
+ return types.CheckResponse{Allowed: tt.shouldAllow}, nil
+ }
+
+ accessClient := &fakeAccessClient{checkFunc: checkFunc}
+ authz := NewTeamBindingAuthorizer(accessClient)
+ ctx := types.WithAuthInfo(context.Background(), user)
+
+ err := authz.BeforeCreate(ctx, binding)
+ if tt.shouldAllow {
+ require.NoError(t, err)
+ } else {
+ require.Error(t, err)
+ }
+ require.True(t, accessClient.checkCalled)
+ })
+ }
+}
+
+func TestTeamBinding_BeforeUpdate(t *testing.T) {
+ binding := newTeamBinding("team-1", "binding-1", "other")
+
+ tests := []struct {
+ name string
+ shouldAllow bool
+ }{
+ {
+ name: "allow update",
+ shouldAllow: true,
+ },
+ {
+ name: "deny update",
+ shouldAllow: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ checkFunc := func(id types.AuthInfo, req *types.CheckRequest, folder string) (types.CheckResponse, error) {
+ require.Equal(t, "org-2", req.Namespace)
+ require.Equal(t, iamv0.GROUP, req.Group)
+ require.Equal(t, iamv0.TeamResourceInfo.GetName(), req.Resource)
+ require.Equal(t, "team-1", req.Name)
+ require.Equal(t, utils.VerbSetPermissions, req.Verb)
+
+ return types.CheckResponse{Allowed: tt.shouldAllow}, nil
+ }
+
+ accessClient := &fakeAccessClient{checkFunc: checkFunc}
+ authz := NewTeamBindingAuthorizer(accessClient)
+ ctx := types.WithAuthInfo(context.Background(), user)
+
+ err := authz.BeforeUpdate(ctx, binding)
+ if tt.shouldAllow {
+ require.NoError(t, err)
+ } else {
+ require.Error(t, err)
+ }
+ require.True(t, accessClient.checkCalled)
+ })
+ }
+}
+
+func TestTeamBinding_BeforeDelete(t *testing.T) {
+ binding := newTeamBinding("team-1", "binding-1", "other")
+
+ tests := []struct {
+ name string
+ shouldAllow bool
+ }{
+ {
+ name: "allow delete",
+ shouldAllow: true,
+ },
+ {
+ name: "deny delete",
+ shouldAllow: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ checkFunc := func(id types.AuthInfo, req *types.CheckRequest, folder string) (types.CheckResponse, error) {
+ require.Equal(t, "org-2", req.Namespace)
+ require.Equal(t, iamv0.GROUP, req.Group)
+ require.Equal(t, iamv0.TeamResourceInfo.GetName(), req.Resource)
+ require.Equal(t, "team-1", req.Name)
+ require.Equal(t, utils.VerbSetPermissions, req.Verb)
+
+ return types.CheckResponse{Allowed: tt.shouldAllow}, nil
+ }
+
+ accessClient := &fakeAccessClient{checkFunc: checkFunc}
+ authz := NewTeamBindingAuthorizer(accessClient)
+ ctx := types.WithAuthInfo(context.Background(), user)
+
+ err := authz.BeforeDelete(ctx, binding)
+ if tt.shouldAllow {
+ require.NoError(t, err)
+ } else {
+ require.Error(t, err)
+ }
+ require.True(t, accessClient.checkCalled)
+ })
+ }
+}
diff --git a/pkg/registry/apis/iam/register.go b/pkg/registry/apis/iam/register.go
index bf29142648f..4ae2ef164a5 100644
--- a/pkg/registry/apis/iam/register.go
+++ b/pkg/registry/apis/iam/register.go
@@ -376,7 +376,7 @@ func (b *IdentityAccessManagementAPIBuilder) UpdateTeamBindingsAPIGroup(opts bui
if err != nil {
return err
}
- storage[teamBindingResource.StoragePath()] = teamBindingUniStore
+ var teamBindingStore storewrapper.K8sStorage = teamBindingUniStore
// Only teamBindingStore exposes the AfterCreate, AfterDelete, and BeginUpdate hooks
if enableZanzanaSync {
@@ -391,8 +391,16 @@ func (b *IdentityAccessManagementAPIBuilder) UpdateTeamBindingsAPIGroup(opts bui
if err != nil {
return err
}
- storage[teamBindingResource.StoragePath()] = dw
+
+ var ok bool
+ teamBindingStore, ok = dw.(storewrapper.K8sStorage)
+ if !ok {
+ return fmt.Errorf("expected storewrapper.K8sStorage, got %T", dw)
+ }
}
+
+ authzWrapper := storewrapper.New(teamBindingStore, iamauthorizer.NewTeamBindingAuthorizer(b.accessClient))
+ storage[teamBindingResource.StoragePath()] = authzWrapper
return nil
}
diff --git a/pkg/server/ring.go b/pkg/server/ring.go
index 90026e58391..4dc261d68c5 100644
--- a/pkg/server/ring.go
+++ b/pkg/server/ring.go
@@ -3,6 +3,7 @@ package server
import (
"context"
"fmt"
+ "strconv"
"time"
"github.com/grafana/dskit/flagext"
@@ -15,11 +16,15 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/storage/unified/resource"
+ grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
+ "github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
+ "google.golang.org/grpc/backoff"
+ "google.golang.org/grpc/codes"
"google.golang.org/grpc/health/grpc_health_v1"
)
@@ -111,14 +116,25 @@ func newClientPool(clientCfg grpcclient.Config, log log.Logger, reg prometheus.R
Help: "Time spent executing requests to resource server.",
Buckets: prometheus.ExponentialBuckets(0.008, 4, 7),
}, []string{"operation", "status_code"})
+ factoryRequestRetries := promauto.With(reg).NewCounterVec(prometheus.CounterOpts{
+ Name: "resource_server_client_request_retries_total",
+ Help: "Total number of retries for requests to the resource server.",
+ }, []string{"operation"})
factory := ringclient.PoolInstFunc(func(inst ring.InstanceDesc) (ringclient.PoolClient, error) {
unaryInterceptors, streamInterceptors := grpcclient.Instrument(factoryRequestDuration)
+
+ // Add retry interceptors for transient connection issues
+ unaryInterceptors = append(unaryInterceptors, ringClientRetryInterceptor())
+ unaryInterceptors = append(unaryInterceptors, ringClientRetryInstrument(factoryRequestRetries))
+
opts, err := clientCfg.DialOption(unaryInterceptors, streamInterceptors, nil)
if err != nil {
return nil, err
}
+ opts = append(opts, connectionBackoffOptions())
+
conn, err := grpc.NewClient(inst.Addr, opts...)
if err != nil {
return nil, fmt.Errorf("failed to dial resource server %s %s: %s", inst.Id, inst.Addr, err)
@@ -135,3 +151,40 @@ func newClientPool(clientCfg grpcclient.Config, log log.Logger, reg prometheus.R
return ringclient.NewPool(resource.RingName, poolCfg, nil, factory, clientsCount, log)
}
+
+// ringClientRetryInterceptor creates an interceptor to perform retries for unary methods.
+// It retries on ResourceExhausted and Unavailable codes, which are typical for
+// transient connection issues and rate limiting.
+func ringClientRetryInterceptor() grpc.UnaryClientInterceptor {
+ return grpc_retry.UnaryClientInterceptor(
+ grpc_retry.WithMax(3),
+ grpc_retry.WithBackoff(grpc_retry.BackoffExponentialWithJitter(time.Second, 0.1)),
+ grpc_retry.WithCodes(codes.ResourceExhausted, codes.Unavailable),
+ )
+}
+
+// ringClientRetryInstrument creates an interceptor to count retry attempts for metrics.
+func ringClientRetryInstrument(metric *prometheus.CounterVec) grpc.UnaryClientInterceptor {
+ return func(ctx context.Context, method string, req, resp interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
+ // We can tell if a call is a retry by checking the retry attempt metadata.
+ attempt, err := strconv.Atoi(metautils.ExtractOutgoing(ctx).Get(grpc_retry.AttemptMetadataKey))
+ if err == nil && attempt > 0 {
+ metric.WithLabelValues(method).Inc()
+ }
+ return invoker(ctx, method, req, resp, cc, opts...)
+ }
+}
+
+// connectionBackoffOptions configures connection backoff parameters for faster recovery from
+// transient connection failures (e.g., during pod restarts).
+func connectionBackoffOptions() grpc.DialOption {
+ return grpc.WithConnectParams(grpc.ConnectParams{
+ Backoff: backoff.Config{
+ BaseDelay: 100 * time.Millisecond,
+ Multiplier: 1.6,
+ Jitter: 0.2,
+ MaxDelay: 10 * time.Second,
+ },
+ MinConnectTimeout: 5 * time.Second,
+ })
+}
diff --git a/pkg/services/apiserver/options/storage.go b/pkg/services/apiserver/options/storage.go
index 28f6e1046ab..057858caa01 100644
--- a/pkg/services/apiserver/options/storage.go
+++ b/pkg/services/apiserver/options/storage.go
@@ -11,11 +11,16 @@ import (
"github.com/spf13/pflag"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"google.golang.org/grpc"
+ "google.golang.org/grpc/backoff"
+ "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
+ "google.golang.org/grpc/keepalive"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/options"
"k8s.io/client-go/rest"
+ grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
+
apiserverrest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/infra/tracing"
secret "github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
@@ -232,19 +237,16 @@ func (o *StorageOptions) ApplyTo(serverConfig *genericapiserver.RecommendedConfi
if o.StorageType != StorageTypeUnifiedGrpc {
return nil
}
- conn, err := grpc.NewClient(o.Address,
- grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
- grpc.WithTransportCredentials(insecure.NewCredentials()),
- )
+
+ grpcOpts := o.buildGrpcDialOptions()
+
+ conn, err := grpc.NewClient(o.Address, grpcOpts...)
if err != nil {
return err
}
var indexConn *grpc.ClientConn
if o.SearchServerAddress != "" {
- indexConn, err = grpc.NewClient(o.SearchServerAddress,
- grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
- grpc.WithTransportCredentials(insecure.NewCredentials()),
- )
+ indexConn, err = grpc.NewClient(o.SearchServerAddress, grpcOpts...)
if err != nil {
return err
}
@@ -293,3 +295,42 @@ func (o *StorageOptions) ApplyTo(serverConfig *genericapiserver.RecommendedConfi
serverConfig.RESTOptionsGetter = getter
return nil
}
+
+// buildGrpcDialOptions creates gRPC dial options with resilience mechanisms:
+// - Round-robin load balancing with client-side health checking
+// - Retry interceptor for transient connection issues
+// - Keepalive for long-lived connections
+func (o *StorageOptions) buildGrpcDialOptions() []grpc.DialOption {
+ // Retry interceptor for transient connection issues (codes.Unavailable includes connection refused)
+ retryInterceptor := grpc_retry.UnaryClientInterceptor(
+ grpc_retry.WithMax(3),
+ grpc_retry.WithBackoff(grpc_retry.BackoffExponentialWithJitter(time.Second, 0.5)),
+ grpc_retry.WithCodes(codes.ResourceExhausted, codes.Unavailable),
+ )
+
+ opts := []grpc.DialOption{
+ grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
+ grpc.WithTransportCredentials(insecure.NewCredentials()),
+ grpc.WithChainUnaryInterceptor(retryInterceptor),
+ grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`),
+ grpc.WithConnectParams(grpc.ConnectParams{
+ Backoff: backoff.Config{
+ BaseDelay: 100 * time.Millisecond,
+ Multiplier: 1.6,
+ Jitter: 0.2,
+ MaxDelay: 10 * time.Second,
+ },
+ MinConnectTimeout: 5 * time.Second,
+ }),
+ }
+
+ if o.GrpcClientKeepaliveTime > 0 {
+ opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{
+ Time: o.GrpcClientKeepaliveTime,
+ Timeout: 10 * time.Second,
+ PermitWithoutStream: true,
+ }))
+ }
+
+ return opts
+}
diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go
index 37a85003f41..d86e648c3bb 100644
--- a/pkg/services/featuremgmt/registry.go
+++ b/pkg/services/featuremgmt/registry.go
@@ -49,7 +49,7 @@ var (
Name: "lokiExperimentalStreaming",
Description: "Support new streaming approach for loki (prototype, needs special loki build)",
Stage: FeatureStageExperimental,
- Owner: grafanaObservabilityLogsSquad,
+ Owner: grafanaOSSBigTent,
},
{
Name: "featureHighlights",
@@ -177,7 +177,7 @@ var (
Name: "lokiLogsDataplane",
Description: "Changes logs responses from Loki to be compliant with the dataplane specification.",
Stage: FeatureStageExperimental,
- Owner: grafanaObservabilityLogsSquad,
+ Owner: grafanaOSSBigTent,
},
{
Name: "disableSSEDataplane",
@@ -340,7 +340,7 @@ var (
Description: "Enables running Loki queries in parallel",
Stage: FeatureStagePrivatePreview,
FrontendOnly: false,
- Owner: grafanaObservabilityLogsSquad,
+ Owner: grafanaOSSBigTent,
},
{
Name: "externalServiceAccounts",
@@ -574,8 +574,8 @@ var (
},
{
Name: "dashboardNewLayouts",
- Description: "Enables experimental new dashboard layouts",
- Stage: FeatureStageExperimental,
+ Description: "Enables new dashboard layouts",
+ Stage: FeatureStagePublicPreview,
FrontendOnly: false, // The restore backend feature changes behavior based on this flag
Owner: grafanaDashboardsSquad,
},
@@ -745,7 +745,7 @@ var (
Name: "logQLScope",
Description: "In-development feature that will allow injection of labels into loki queries.",
Stage: FeatureStagePrivatePreview,
- Owner: grafanaObservabilityLogsSquad,
+ Owner: grafanaOSSBigTent,
Expression: "false",
HideFromDocs: true,
},
@@ -879,6 +879,13 @@ var (
Owner: grafanaAlertingSquad,
FrontendOnly: true,
},
+ {
+ Name: "alertingNavigationV2",
+ Description: "Enables the new Alerting navigation structure with improved menu grouping",
+ Stage: FeatureStageExperimental,
+ Owner: grafanaAlertingSquad,
+ FrontendOnly: false,
+ },
{
Name: "alertingSavedSearches",
Description: "Enables saved searches for alert rules list",
@@ -981,7 +988,8 @@ var (
Stage: FeatureStageDeprecated,
Owner: grafanaPartnerPluginsSquad,
Expression: "true", // Enabled by default for now
- }, {
+ },
+ {
Name: "alertingFilterV2",
Description: "Enable the new alerting search experience",
Stage: FeatureStageExperimental,
@@ -1073,13 +1081,6 @@ var (
Stage: FeatureStageExperimental,
Owner: identityAccessTeam,
},
- {
- Name: "unifiedStorageSearchSprinkles",
- Description: "Enable sprinkles on unified storage search",
- Stage: FeatureStageExperimental,
- Owner: grafanaSearchAndStorageSquad,
- HideFromDocs: true,
- },
{
Name: "managedDualWriter",
Description: "Pick the dual write mode from database configs",
@@ -1267,7 +1268,7 @@ var (
Name: "lokiLabelNamesQueryApi",
Description: "Defaults to using the Loki `/labels` API instead of `/series`",
Stage: FeatureStageGeneralAvailability,
- Owner: grafanaObservabilityLogsSquad,
+ Owner: grafanaOSSBigTent,
Expression: "true",
},
{
@@ -1639,6 +1640,15 @@ var (
FrontendOnly: true,
Expression: "false",
},
+ {
+ Name: "experimentRecentlyViewedDashboards",
+ Description: "A/A test for recently viewed dashboards feature",
+ Stage: FeatureStageExperimental,
+ Owner: grafanaFrontendSearchNavOrganise,
+ FrontendOnly: true,
+ HideFromDocs: true,
+ Expression: "false",
+ },
{
Name: "alertEnrichment",
Description: "Enable configuration of alert enrichments in Grafana Cloud.",
@@ -2074,6 +2084,14 @@ var (
Owner: grafanaObservabilityTracesAndProfilingSquad,
FrontendOnly: false,
},
+ {
+ Name: "alertingSyncDispatchTimer",
+ Description: "Use synchronized dispatch timer to minimize duplicate notifications across alertmanager HA pods",
+ Stage: FeatureStageExperimental,
+ Owner: grafanaAlertingSquad,
+ RequiresRestart: true,
+ HideFromDocs: true,
+ },
}
)
diff --git a/pkg/services/featuremgmt/toggles-gitlog.csv b/pkg/services/featuremgmt/toggles-gitlog.csv
index c924b05d7d5..644a1627577 100644
--- a/pkg/services/featuremgmt/toggles-gitlog.csv
+++ b/pkg/services/featuremgmt/toggles-gitlog.csv
@@ -409,7 +409,6 @@ lokiLabelNamesQueryApi,2024-12-13T14:31:41Z,,5ac7443fcec0db412d3333044a82c2c26b5
kubernetesCliDashboards,2024-12-13T22:55:43Z,2025-02-18T23:11:26Z,8f6e9f8ed0a5024a510cc337c9f1e6972bfb23d4,Stephanie Hingtgen
useV2DashboardsAPI,2024-12-17T21:17:09Z,2025-03-12T17:43:32Z,070f0e4457c5967102ef157197073dc2662f6fb8,Dominik Prokop
investigationsBackend,2024-12-18T08:31:03Z,,f46c07aba7b6faccd2ecafc83051d1410cacc867,Jackson Coelho
-unifiedStorageSearchSprinkles,2024-12-18T17:00:54Z,,4837585cab0fd84184a8c6f5d6891f442a2b95f1,owensmallwood
prometheusSpecialCharsInLabelValues,2024-12-18T21:31:08Z,,721c50a304588ebd7cea76e301ec0f68a5a55d68,Nick Richmond
unifiedStorageSearchUI,2024-12-19T18:21:48Z,,a8f347144ddc16f2033fdeb4f3474e49239ba7ab,Scott Lepper
playlistsReconciler,2024-12-20T03:09:31Z,,24bf337c562dc9b9d8684cc9acb7ea171ea83414,Charandas
diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv
index c8ab9b7951c..f5ced950d10 100644
--- a/pkg/services/featuremgmt/toggles_gen.csv
+++ b/pkg/services/featuremgmt/toggles_gen.csv
@@ -3,7 +3,7 @@ disableEnvelopeEncryption,GA,@grafana/grafana-operator-experience-squad,false,fa
panelTitleSearch,preview,@grafana/search-and-storage,false,false,false
publicDashboardsEmailSharing,preview,@grafana/grafana-operator-experience-squad,false,false,false
publicDashboardsScene,GA,@grafana/grafana-operator-experience-squad,false,false,true
-lokiExperimentalStreaming,experimental,@grafana/observability-logs,false,false,false
+lokiExperimentalStreaming,experimental,@grafana/oss-big-tent,false,false,false
featureHighlights,GA,@grafana/grafana-operator-experience-squad,false,false,false
storage,experimental,@grafana/search-and-storage,false,false,false
canvasPanelNesting,experimental,@grafana/dataviz-squad,false,false,true
@@ -22,7 +22,7 @@ starsFromAPIServer,experimental,@grafana/grafana-search-navigate-organise,false,
kubernetesStars,experimental,@grafana/grafana-app-platform-squad,false,true,false
influxqlStreamingParser,experimental,@grafana/partner-datasources,false,false,false
influxdbRunQueriesInParallel,privatePreview,@grafana/partner-datasources,false,false,false
-lokiLogsDataplane,experimental,@grafana/observability-logs,false,false,false
+lokiLogsDataplane,experimental,@grafana/oss-big-tent,false,false,false
disableSSEDataplane,experimental,@grafana/grafana-datasources-core-services,false,false,false
renderAuthJWT,preview,@grafana/grafana-operator-experience-squad,false,false,false
refactorVariablesTimeRange,preview,@grafana/dashboards-squad,false,false,false
@@ -45,7 +45,7 @@ aiGeneratedDashboardChanges,experimental,@grafana/dashboards-squad,false,false,t
reportingRetries,preview,@grafana/grafana-operator-experience-squad,false,true,false
reportingCsvEncodingOptions,experimental,@grafana/grafana-operator-experience-squad,false,false,false
sseGroupByDatasource,experimental,@grafana/grafana-datasources-core-services,false,false,false
-lokiRunQueriesInParallel,privatePreview,@grafana/observability-logs,false,false,false
+lokiRunQueriesInParallel,privatePreview,@grafana/oss-big-tent,false,false,false
externalServiceAccounts,preview,@grafana/identity-access-team,false,false,false
enableNativeHTTPHistogram,experimental,@grafana/grafana-backend-services-squad,false,true,false
disableClassicHTTPHistogram,experimental,@grafana/grafana-backend-services-squad,false,true,false
@@ -79,7 +79,7 @@ annotationPermissionUpdate,GA,@grafana/identity-access-team,false,false,false
dashboardSceneForViewers,GA,@grafana/dashboards-squad,false,false,true
dashboardSceneSolo,GA,@grafana/dashboards-squad,false,false,true
dashboardScene,GA,@grafana/dashboards-squad,false,false,true
-dashboardNewLayouts,experimental,@grafana/dashboards-squad,false,false,false
+dashboardNewLayouts,preview,@grafana/dashboards-squad,false,false,false
dashboardUndoRedo,experimental,@grafana/dashboards-squad,false,false,true
unlimitedLayoutsNesting,experimental,@grafana/dashboards-squad,false,false,true
drilldownRecommendations,experimental,@grafana/dashboards-squad,false,false,true
@@ -102,7 +102,7 @@ alertingSaveStateCompressed,preview,@grafana/alerting-squad,false,false,false
scopeApi,experimental,@grafana/grafana-app-platform-squad,false,false,false
useScopeSingleNodeEndpoint,experimental,@grafana/grafana-operator-experience-squad,false,false,true
useMultipleScopeNodesEndpoint,experimental,@grafana/grafana-operator-experience-squad,false,false,true
-logQLScope,privatePreview,@grafana/observability-logs,false,false,false
+logQLScope,privatePreview,@grafana/oss-big-tent,false,false,false
sqlExpressions,preview,@grafana/grafana-datasources-core-services,false,false,false
sqlExpressionsColumnAutoComplete,experimental,@grafana/datapro,false,false,true
kubernetesAggregator,experimental,@grafana/grafana-app-platform-squad,false,true,false
@@ -121,6 +121,7 @@ dashboardLibrary,experimental,@grafana/sharing-squad,false,false,false
suggestedDashboards,experimental,@grafana/sharing-squad,false,false,false
dashboardTemplates,preview,@grafana/sharing-squad,false,false,false
alertingListViewV2,privatePreview,@grafana/alerting-squad,false,false,true
+alertingNavigationV2,experimental,@grafana/alerting-squad,false,false,false
alertingSavedSearches,experimental,@grafana/alerting-squad,false,false,true
alertingDisableSendAlertsExternal,experimental,@grafana/alerting-squad,false,false,false
preserveDashboardStateWhenNavigating,experimental,@grafana/dashboards-squad,false,false,false
@@ -148,7 +149,6 @@ alertingQueryAndExpressionsStepMode,GA,@grafana/alerting-squad,false,false,true
improvedExternalSessionHandling,GA,@grafana/identity-access-team,false,false,false
useSessionStorageForRedirection,GA,@grafana/identity-access-team,false,false,false
rolePickerDrawer,experimental,@grafana/identity-access-team,false,false,false
-unifiedStorageSearchSprinkles,experimental,@grafana/search-and-storage,false,false,false
managedDualWriter,experimental,@grafana/search-and-storage,false,false,false
pluginsSriChecks,GA,@grafana/plugins-platform-backend,false,false,false
unifiedStorageBigObjectsSupport,experimental,@grafana/search-and-storage,false,false,false
@@ -174,7 +174,7 @@ alertingAIAnalyzeCentralStateHistory,experimental,@grafana/alerting-squad,false,
alertingNotificationsStepMode,GA,@grafana/alerting-squad,false,false,true
unifiedStorageSearchUI,experimental,@grafana/search-and-storage,false,false,false
elasticsearchCrossClusterSearch,GA,@grafana/partner-datasources,false,false,false
-lokiLabelNamesQueryApi,GA,@grafana/observability-logs,false,false,false
+lokiLabelNamesQueryApi,GA,@grafana/oss-big-tent,false,false,false
k8SFolderCounts,experimental,@grafana/search-and-storage,false,false,false
k8SFolderMove,experimental,@grafana/search-and-storage,false,false,false
improvedExternalSessionHandlingSAML,GA,@grafana/identity-access-team,false,false,false
@@ -225,6 +225,7 @@ kubernetesAuthnMutation,experimental,@grafana/identity-access-team,false,false,f
kubernetesExternalGroupMapping,experimental,@grafana/identity-access-team,false,false,false
restoreDashboards,experimental,@grafana/grafana-search-navigate-organise,false,false,false
recentlyViewedDashboards,experimental,@grafana/grafana-search-navigate-organise,false,false,true
+experimentRecentlyViewedDashboards,experimental,@grafana/grafana-search-navigate-organise,false,false,true
alertEnrichment,experimental,@grafana/alerting-squad,false,false,false
alertEnrichmentMultiStep,experimental,@grafana/alerting-squad,false,false,false
alertEnrichmentConditional,experimental,@grafana/alerting-squad,false,false,false
@@ -281,3 +282,4 @@ multiPropsVariables,experimental,@grafana/dashboards-squad,false,false,true
smoothingTransformation,experimental,@grafana/datapro,false,false,true
secretsManagementAppPlatformAwsKeeper,experimental,@grafana/grafana-operator-experience-squad,false,false,false
profilesExemplars,experimental,@grafana/observability-traces-and-profiling,false,false,false
+alertingSyncDispatchTimer,experimental,@grafana/alerting-squad,false,true,false
diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go
index b6731bb61fb..f5dca6fd3dc 100644
--- a/pkg/services/featuremgmt/toggles_gen.go
+++ b/pkg/services/featuremgmt/toggles_gen.go
@@ -260,7 +260,7 @@ const (
FlagAnnotationPermissionUpdate = "annotationPermissionUpdate"
// FlagDashboardNewLayouts
- // Enables experimental new dashboard layouts
+ // Enables new dashboard layouts
FlagDashboardNewLayouts = "dashboardNewLayouts"
// FlagPdfTables
@@ -371,6 +371,10 @@ const (
// Enables a flow to get started with a new dashboard from a template
FlagDashboardTemplates = "dashboardTemplates"
+ // FlagAlertingNavigationV2
+ // Enables the new Alerting navigation structure with improved menu grouping
+ FlagAlertingNavigationV2 = "alertingNavigationV2"
+
// FlagAlertingDisableSendAlertsExternal
// Disables the ability to send alerts to an external Alertmanager datasource.
FlagAlertingDisableSendAlertsExternal = "alertingDisableSendAlertsExternal"
@@ -455,10 +459,6 @@ const (
// Enables the new role picker drawer design
FlagRolePickerDrawer = "rolePickerDrawer"
- // FlagUnifiedStorageSearchSprinkles
- // Enable sprinkles on unified storage search
- FlagUnifiedStorageSearchSprinkles = "unifiedStorageSearchSprinkles"
-
// FlagManagedDualWriter
// Pick the dual write mode from database configs
FlagManagedDualWriter = "managedDualWriter"
@@ -797,4 +797,8 @@ const (
// FlagProfilesExemplars
// Enables profiles exemplars support in profiles drilldown
FlagProfilesExemplars = "profilesExemplars"
+
+ // FlagAlertingSyncDispatchTimer
+ // Use synchronized dispatch timer to minimize duplicate notifications across alertmanager HA pods
+ FlagAlertingSyncDispatchTimer = "alertingSyncDispatchTimer"
)
diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json
index eaf038f8be9..bcfabb70a74 100644
--- a/pkg/services/featuremgmt/toggles_gen.json
+++ b/pkg/services/featuremgmt/toggles_gen.json
@@ -348,6 +348,18 @@
"expression": "true"
}
},
+ {
+ "metadata": {
+ "name": "alertingNavigationV2",
+ "resourceVersion": "1768320918269",
+ "creationTimestamp": "2026-01-13T16:15:18Z"
+ },
+ "spec": {
+ "description": "Enables the new Alerting navigation structure with improved menu grouping",
+ "stage": "experimental",
+ "codeowner": "@grafana/alerting-squad"
+ }
+ },
{
"metadata": {
"name": "alertingNotificationHistory",
@@ -511,6 +523,20 @@
"frontend": true
}
},
+ {
+ "metadata": {
+ "name": "alertingSyncDispatchTimer",
+ "resourceVersion": "1766161788928",
+ "creationTimestamp": "2025-12-19T16:29:48Z"
+ },
+ "spec": {
+ "description": "Use synchronized dispatch timer to minimize duplicate notifications across alertmanager HA pods",
+ "stage": "experimental",
+ "codeowner": "@grafana/alerting-squad",
+ "requiresRestart": true,
+ "hideFromDocs": true
+ }
+ },
{
"metadata": {
"name": "alertingTriage",
@@ -662,7 +688,8 @@
"metadata": {
"name": "auditLoggingAppPlatform",
"resourceVersion": "1767013056996",
- "creationTimestamp": "2025-12-29T12:57:36Z"
+ "creationTimestamp": "2025-12-29T12:57:36Z",
+ "deletionTimestamp": "2026-01-06T09:18:36Z"
},
"spec": {
"description": "Enable audit logging with Kubernetes under app platform",
@@ -1015,12 +1042,15 @@
{
"metadata": {
"name": "dashboardNewLayouts",
- "resourceVersion": "1764664939750",
- "creationTimestamp": "2024-10-23T08:55:45Z"
+ "resourceVersion": "1768382835527",
+ "creationTimestamp": "2024-10-23T08:55:45Z",
+ "annotations": {
+ "grafana.app/updatedTimestamp": "2026-01-14 09:27:15.527103 +0000 UTC"
+ }
},
"spec": {
- "description": "Enables experimental new dashboard layouts",
- "stage": "experimental",
+ "description": "Enables new dashboard layouts",
+ "stage": "preview",
"codeowner": "@grafana/dashboards-squad"
}
},
@@ -1365,6 +1395,21 @@
"hideFromDocs": true
}
},
+ {
+ "metadata": {
+ "name": "experimentRecentlyViewedDashboards",
+ "resourceVersion": "1768214542023",
+ "creationTimestamp": "2026-01-12T10:42:22Z"
+ },
+ "spec": {
+ "description": "A/A test for recently viewed dashboards feature",
+ "stage": "experimental",
+ "codeowner": "@grafana/grafana-search-navigate-organise",
+ "frontend": true,
+ "hideFromDocs": true,
+ "expression": "false"
+ }
+ },
{
"metadata": {
"name": "exploreLogsAggregatedMetrics",
@@ -2220,13 +2265,16 @@
{
"metadata": {
"name": "logQLScope",
- "resourceVersion": "1764664939750",
- "creationTimestamp": "2024-11-11T11:53:24Z"
+ "resourceVersion": "1768317398145",
+ "creationTimestamp": "2024-11-11T11:53:24Z",
+ "annotations": {
+ "grafana.app/updatedTimestamp": "2026-01-13 15:16:38.145488 +0000 UTC"
+ }
},
"spec": {
"description": "In-development feature that will allow injection of labels into loki queries.",
"stage": "privatePreview",
- "codeowner": "@grafana/observability-logs",
+ "codeowner": "@grafana/oss-big-tent",
"hideFromDocs": true,
"expression": "false"
}
@@ -2302,38 +2350,47 @@
{
"metadata": {
"name": "lokiExperimentalStreaming",
- "resourceVersion": "1764664939750",
- "creationTimestamp": "2023-06-19T10:03:51Z"
+ "resourceVersion": "1768317398145",
+ "creationTimestamp": "2023-06-19T10:03:51Z",
+ "annotations": {
+ "grafana.app/updatedTimestamp": "2026-01-13 15:16:38.145488 +0000 UTC"
+ }
},
"spec": {
"description": "Support new streaming approach for loki (prototype, needs special loki build)",
"stage": "experimental",
- "codeowner": "@grafana/observability-logs"
+ "codeowner": "@grafana/oss-big-tent"
}
},
{
"metadata": {
"name": "lokiLabelNamesQueryApi",
- "resourceVersion": "1764664939750",
- "creationTimestamp": "2024-12-13T14:31:41Z"
+ "resourceVersion": "1768317398145",
+ "creationTimestamp": "2024-12-13T14:31:41Z",
+ "annotations": {
+ "grafana.app/updatedTimestamp": "2026-01-13 15:16:38.145488 +0000 UTC"
+ }
},
"spec": {
"description": "Defaults to using the Loki `/labels` API instead of `/series`",
"stage": "GA",
- "codeowner": "@grafana/observability-logs",
+ "codeowner": "@grafana/oss-big-tent",
"expression": "true"
}
},
{
"metadata": {
"name": "lokiLogsDataplane",
- "resourceVersion": "1764664939750",
- "creationTimestamp": "2023-07-13T07:58:00Z"
+ "resourceVersion": "1768317398145",
+ "creationTimestamp": "2023-07-13T07:58:00Z",
+ "annotations": {
+ "grafana.app/updatedTimestamp": "2026-01-13 15:16:38.145488 +0000 UTC"
+ }
},
"spec": {
"description": "Changes logs responses from Loki to be compliant with the dataplane specification.",
"stage": "experimental",
- "codeowner": "@grafana/observability-logs"
+ "codeowner": "@grafana/oss-big-tent"
}
},
{
@@ -2366,13 +2423,16 @@
{
"metadata": {
"name": "lokiRunQueriesInParallel",
- "resourceVersion": "1764664939750",
- "creationTimestamp": "2023-09-19T09:34:01Z"
+ "resourceVersion": "1768317398145",
+ "creationTimestamp": "2023-09-19T09:34:01Z",
+ "annotations": {
+ "grafana.app/updatedTimestamp": "2026-01-13 15:16:38.145488 +0000 UTC"
+ }
},
"spec": {
"description": "Enables running Loki queries in parallel",
"stage": "privatePreview",
- "codeowner": "@grafana/observability-logs"
+ "codeowner": "@grafana/oss-big-tent"
}
},
{
@@ -3736,19 +3796,6 @@
"hideFromDocs": true
}
},
- {
- "metadata": {
- "name": "unifiedStorageSearchSprinkles",
- "resourceVersion": "1764664939750",
- "creationTimestamp": "2024-12-18T17:00:54Z"
- },
- "spec": {
- "description": "Enable sprinkles on unified storage search",
- "stage": "experimental",
- "codeowner": "@grafana/search-and-storage",
- "hideFromDocs": true
- }
- },
{
"metadata": {
"name": "unifiedStorageSearchUI",
diff --git a/pkg/services/navtree/navtreeimpl/admin.go b/pkg/services/navtree/navtreeimpl/admin.go
index ca91ab4f915..c1da10781d6 100644
--- a/pkg/services/navtree/navtreeimpl/admin.go
+++ b/pkg/services/navtree/navtreeimpl/admin.go
@@ -54,8 +54,7 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
}
//nolint:staticcheck // not yet migrated to OpenFeature
if c.HasRole(identity.RoleAdmin) &&
- (s.cfg.StackID == "" || // show OnPrem even when provisioning is disabled
- s.features.IsEnabledGlobally(featuremgmt.FlagProvisioning)) {
+ s.features.IsEnabledGlobally(featuremgmt.FlagProvisioning) {
generalNodeLinks = append(generalNodeLinks, &navtree.NavLink{
Text: "Provisioning",
Id: "provisioning",
diff --git a/pkg/services/ngalert/ngalert.go b/pkg/services/ngalert/ngalert.go
index 53e49621117..5177107a602 100644
--- a/pkg/services/ngalert/ngalert.go
+++ b/pkg/services/ngalert/ngalert.go
@@ -213,6 +213,9 @@ func (ng *AlertNG) init() error {
SkipVerify: ng.Cfg.Smtp.SkipVerify,
StaticHeaders: ng.Cfg.Smtp.StaticHeaders,
}
+ runtimeConfig := remoteClient.RuntimeConfig{
+ DispatchTimer: notifier.GetDispatchTimer(ng.FeatureToggles).String(),
+ }
cfg := remote.AlertmanagerConfig{
BasicAuthPassword: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.Password,
@@ -222,6 +225,7 @@ func (ng *AlertNG) init() error {
ExternalURL: ng.Cfg.AppURL,
SmtpConfig: smtpCfg,
Timeout: ng.Cfg.UnifiedAlerting.RemoteAlertmanager.Timeout,
+ RuntimeConfig: runtimeConfig,
}
autogenFn := func(ctx context.Context, logger log.Logger, orgID int64, cfg *definitions.PostableApiAlertingConfig, invalidReceiverAction notifier.InvalidReceiversAction) error {
return notifier.AddAutogenConfig(ctx, logger, ng.store, orgID, cfg, invalidReceiverAction, ng.FeatureToggles)
diff --git a/pkg/services/ngalert/notifier/alertmanager.go b/pkg/services/ngalert/notifier/alertmanager.go
index f192ed88058..6d81d51dc75 100644
--- a/pkg/services/ngalert/notifier/alertmanager.go
+++ b/pkg/services/ngalert/notifier/alertmanager.go
@@ -33,6 +33,9 @@ const (
// How long we keep silences in the kvstore after they've expired.
silenceRetention = 5 * 24 * time.Hour
+
+ // How long we keep flushes in the kvstore after they've expired.
+ flushRetention = 5 * 24 * time.Hour
)
type AlertingStore interface {
@@ -44,8 +47,10 @@ type AlertingStore interface {
type stateStore interface {
SaveSilences(ctx context.Context, st alertingNotify.State) (int64, error)
SaveNotificationLog(ctx context.Context, st alertingNotify.State) (int64, error)
+ SaveFlushLog(ctx context.Context, st alertingNotify.State) (int64, error)
GetSilences(ctx context.Context) (string, error)
GetNotificationLog(ctx context.Context) (string, error)
+ GetFlushLog(ctx context.Context) (string, error)
}
type alertmanager struct {
@@ -101,6 +106,10 @@ func NewAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store A
if err != nil {
return nil, err
}
+ flushLog, err := stateStore.GetFlushLog(ctx)
+ if err != nil {
+ return nil, err
+ }
silencesOptions := maintenanceOptions{
initialState: silences,
@@ -123,12 +132,29 @@ func NewAlertmanager(ctx context.Context, orgID int64, cfg *setting.Cfg, store A
}
l := log.New("ngalert.notifier")
+ dispatchTimer := GetDispatchTimer(featureToggles)
+
+ var flushLogOptions *maintenanceOptions
+ if dispatchTimer == alertingNotify.DispatchTimerSync {
+ flushLogOptions = &maintenanceOptions{
+ initialState: flushLog,
+ retention: flushRetention,
+ maintenanceFrequency: maintenanceInterval,
+ maintenanceFunc: func(state alertingNotify.State) (int64, error) {
+ // Detached context here is to make sure that when the service is shut down the persist operation is executed.
+ return stateStore.SaveFlushLog(context.Background(), state)
+ },
+ }
+ }
+
opts := alertingNotify.GrafanaAlertmanagerOpts{
ExternalURL: cfg.AppURL,
AlertStoreCallback: nil,
PeerTimeout: cfg.UnifiedAlerting.HAPeerTimeout,
Silences: silencesOptions,
Nflog: nflogOptions,
+ FlushLog: flushLogOptions,
+ DispatchTimer: dispatchTimer,
Limits: alertingNotify.Limits{
MaxSilences: cfg.UnifiedAlerting.AlertmanagerMaxSilencesCount,
MaxSilenceSizeBytes: cfg.UnifiedAlerting.AlertmanagerMaxSilenceSizeBytes,
diff --git a/pkg/services/ngalert/notifier/dispatch_timer.go b/pkg/services/ngalert/notifier/dispatch_timer.go
new file mode 100644
index 00000000000..04eaf8cb296
--- /dev/null
+++ b/pkg/services/ngalert/notifier/dispatch_timer.go
@@ -0,0 +1,16 @@
+package notifier
+
+import (
+ alertingNotify "github.com/grafana/alerting/notify"
+ "github.com/grafana/grafana/pkg/services/featuremgmt"
+)
+
+// GetDispatchTimer returns the appropriate dispatch timer based on feature toggles.
+func GetDispatchTimer(features featuremgmt.FeatureToggles) (dt alertingNotify.DispatchTimer) {
+ //nolint:staticcheck // not yet migrated to OpenFeature
+ enabled := features.IsEnabledGlobally(featuremgmt.FlagAlertingSyncDispatchTimer)
+ if enabled {
+ dt = alertingNotify.DispatchTimerSync
+ }
+ return
+}
diff --git a/pkg/services/ngalert/notifier/dispatch_timer_test.go b/pkg/services/ngalert/notifier/dispatch_timer_test.go
new file mode 100644
index 00000000000..3b42a562a32
--- /dev/null
+++ b/pkg/services/ngalert/notifier/dispatch_timer_test.go
@@ -0,0 +1,36 @@
+package notifier
+
+import (
+ "testing"
+
+ alertingNotify "github.com/grafana/alerting/notify"
+ "github.com/grafana/grafana/pkg/services/featuremgmt"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGetDispatchTimer(t *testing.T) {
+ tests := []struct {
+ name string
+ featureFlagValue bool
+ expected alertingNotify.DispatchTimer
+ }{
+ {
+ name: "feature flag enabled returns sync timer",
+ featureFlagValue: true,
+ expected: alertingNotify.DispatchTimerSync,
+ },
+ {
+ name: "feature flag disabled returns default timer",
+ featureFlagValue: false,
+ expected: alertingNotify.DispatchTimerDefault,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ features := featuremgmt.WithFeatures(featuremgmt.FlagAlertingSyncDispatchTimer, tt.featureFlagValue)
+ result := GetDispatchTimer(features)
+ require.Equal(t, tt.expected, result)
+ })
+ }
+}
diff --git a/pkg/services/ngalert/notifier/file_store.go b/pkg/services/ngalert/notifier/file_store.go
index e9628cb536e..3bb128d692d 100644
--- a/pkg/services/ngalert/notifier/file_store.go
+++ b/pkg/services/ngalert/notifier/file_store.go
@@ -15,6 +15,7 @@ const (
KVNamespace = "alertmanager"
NotificationLogFilename = "notifications"
SilencesFilename = "silences"
+ FlushLogFilename = "flushes"
)
// FileStore is in charge of persisting the alertmanager files to the database.
@@ -42,6 +43,10 @@ func (fileStore *FileStore) GetNotificationLog(ctx context.Context) (string, err
return fileStore.contentFor(ctx, NotificationLogFilename)
}
+func (fileStore *FileStore) GetFlushLog(ctx context.Context) (string, error) {
+ return fileStore.contentFor(ctx, FlushLogFilename)
+}
+
// contentFor returns the content for the given Alertmanager kvstore key.
func (fileStore *FileStore) contentFor(ctx context.Context, filename string) (string, error) {
// Then, let's attempt to read it from the database.
@@ -74,6 +79,11 @@ func (fileStore *FileStore) SaveNotificationLog(ctx context.Context, st alerting
return fileStore.persist(ctx, NotificationLogFilename, st)
}
+// SaveFlushLog saves the flush log to the database and returns the size of the unencoded state.
+func (fileStore *FileStore) SaveFlushLog(ctx context.Context, st alertingNotify.State) (int64, error) {
+ return fileStore.persist(ctx, FlushLogFilename, st)
+}
+
// persist takes care of persisting the binary representation of internal state to the database as a base64 encoded string.
func (fileStore *FileStore) persist(ctx context.Context, filename string, st alertingNotify.State) (int64, error) {
var size int64
diff --git a/pkg/services/ngalert/notifier/file_store_test.go b/pkg/services/ngalert/notifier/file_store_test.go
index 1952eb5a0f1..7d4de602a0f 100644
--- a/pkg/services/ngalert/notifier/file_store_test.go
+++ b/pkg/services/ngalert/notifier/file_store_test.go
@@ -106,3 +106,48 @@ func TestFileStore_NotificationLog(t *testing.T) {
t.Errorf("Unexpected Diff: %v", cmp.Diff(newState, decoded))
}
}
+
+func TestFileStore_FlushLog(t *testing.T) {
+ store := fakes.NewFakeKVStore(t)
+ ctx := context.Background()
+ var orgId int64 = 1
+
+ // Initialize kvstore with empty flush log state.
+ initialState := flushLogState{} // FlushLog uses the same structure as nflog
+ decodedState, err := initialState.MarshalBinary()
+ require.NoError(t, err)
+ encodedState := base64.StdEncoding.EncodeToString(decodedState)
+ err = store.Set(ctx, orgId, KVNamespace, FlushLogFilename, encodedState)
+ require.NoError(t, err)
+
+ fs := NewFileStore(orgId, store)
+
+ // Load initial (empty).
+ flushLog, err := fs.GetFlushLog(ctx)
+ require.NoError(t, err)
+ decoded, err := decodeFlushLogState(strings.NewReader(flushLog))
+ require.NoError(t, err)
+ if !cmp.Equal(initialState, decoded) {
+ t.Errorf("Unexpected Diff: %v", cmp.Diff(initialState, decoded))
+ }
+
+ // Save new flush log state.
+ now := time.Now()
+ oneHour := now.Add(time.Hour)
+
+ v1 := createFlushLog(1, now, oneHour)
+ v2 := createFlushLog(2, now, oneHour)
+ newState := flushLogState{1: v1, 2: v2}
+ size, err := fs.SaveFlushLog(ctx, newState)
+ require.NoError(t, err)
+ require.Greater(t, size, int64(0))
+
+ // Load new.
+ flushLog, err = fs.GetFlushLog(ctx)
+ require.NoError(t, err)
+ decoded, err = decodeFlushLogState(strings.NewReader(flushLog))
+ require.NoError(t, err)
+ if !cmp.Equal(newState, decoded) {
+ t.Errorf("Unexpected Diff: %v", cmp.Diff(newState, decoded))
+ }
+}
diff --git a/pkg/services/ngalert/notifier/multiorg_alertmanager.go b/pkg/services/ngalert/notifier/multiorg_alertmanager.go
index 4aa0151d18f..a10aee29ef6 100644
--- a/pkg/services/ngalert/notifier/multiorg_alertmanager.go
+++ b/pkg/services/ngalert/notifier/multiorg_alertmanager.go
@@ -82,6 +82,7 @@ type Alertmanager interface {
type ExternalState struct {
Silences []byte
Nflog []byte
+ FlushLog []byte
}
// StateMerger describes a type that is able to merge external state (nflog, silences) with its own.
@@ -378,7 +379,7 @@ func (moa *MultiOrgAlertmanager) SyncAlertmanagersForOrgs(ctx context.Context, o
func (moa *MultiOrgAlertmanager) cleanupOrphanLocalOrgState(ctx context.Context,
activeOrganizations map[int64]struct{},
) {
- storedFiles := []string{NotificationLogFilename, SilencesFilename}
+ storedFiles := []string{NotificationLogFilename, SilencesFilename, FlushLogFilename}
for _, fileName := range storedFiles {
keys, err := moa.kvStore.Keys(ctx, kvstore.AllOrganizations, KVNamespace, fileName)
if err != nil {
diff --git a/pkg/services/ngalert/notifier/state.go b/pkg/services/ngalert/notifier/state.go
index c8551d2ed1a..04ba9d9a31d 100644
--- a/pkg/services/ngalert/notifier/state.go
+++ b/pkg/services/ngalert/notifier/state.go
@@ -5,5 +5,8 @@ func (am *alertmanager) MergeState(state ExternalState) error {
if err := am.Base.MergeNflog(state.Nflog); err != nil {
return err
}
- return am.Base.MergeSilences(state.Silences)
+ if err := am.Base.MergeSilences(state.Silences); err != nil {
+ return err
+ }
+ return am.Base.MergeFlushLog(state.FlushLog)
}
diff --git a/pkg/services/ngalert/notifier/testing.go b/pkg/services/ngalert/notifier/testing.go
index 9fccf6d2f0d..c2b2190183f 100644
--- a/pkg/services/ngalert/notifier/testing.go
+++ b/pkg/services/ngalert/notifier/testing.go
@@ -11,6 +11,7 @@ import (
"time"
"github.com/matttproud/golang_protobuf_extensions/pbutil"
+ "github.com/prometheus/alertmanager/flushlog/flushlogpb"
"github.com/prometheus/alertmanager/nflog/nflogpb"
"github.com/prometheus/alertmanager/silence/silencepb"
"github.com/prometheus/common/model"
@@ -228,15 +229,13 @@ func (f *FakeOrgStore) FetchOrgIds(_ context.Context) ([]int64, error) {
return f.orgs, nil
}
-type NoValidation struct {
-}
+type NoValidation struct{}
func (n NoValidation) Validate(_ models.NotificationSettings) error {
return nil
}
-type RejectingValidation struct {
-}
+type RejectingValidation struct{}
func (n RejectingValidation) Validate(s models.NotificationSettings) error {
return ErrorReceiverDoesNotExist{ErrorReferenceInvalid: ErrorReferenceInvalid{Reference: s.Receiver}}
@@ -365,6 +364,51 @@ func createNotificationLog(groupKey string, receiverName string, sentAt, expires
}
}
+// https://github.com/grafana/prometheus-alertmanager/blob/main/flushlog/flushlog.go#L136-L136
+type flushLogState map[uint64]*flushlogpb.MeshFlushLog
+
+func (s flushLogState) MarshalBinary() ([]byte, error) {
+ var buf bytes.Buffer
+
+ for _, e := range s {
+ if _, err := pbutil.WriteDelimited(&buf, e); err != nil {
+ return nil, err
+ }
+ }
+ return buf.Bytes(), nil
+}
+
+func createFlushLog(groupFingerprint uint64, ts, expiresAt time.Time) *flushlogpb.MeshFlushLog {
+ return &flushlogpb.MeshFlushLog{
+ FlushLog: &flushlogpb.FlushLog{
+ GroupFingerprint: groupFingerprint,
+ Timestamp: ts,
+ },
+ ExpiresAt: expiresAt,
+ }
+}
+
+// decodeFlushLogState copied from decodeState in prometheus-alertmanager/flushlog/flushlog.go
+func decodeFlushLogState(r io.Reader) (flushLogState, error) {
+ st := flushLogState{}
+ for {
+ var e flushlogpb.MeshFlushLog
+ _, err := pbutil.ReadDelimited(r, &e)
+ if err == nil {
+ if e.FlushLog == nil || e.FlushLog.GroupFingerprint == 0 || e.FlushLog.Timestamp.IsZero() {
+ return nil, errInvalidState
+ }
+ st[e.FlushLog.GroupFingerprint] = &e
+ continue
+ }
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ return nil, err
+ }
+ return st, nil
+}
+
type call struct {
Method string
Args []interface{}
diff --git a/pkg/services/ngalert/remote/alertmanager.go b/pkg/services/ngalert/remote/alertmanager.go
index 60740d935af..07fa5e3138f 100644
--- a/pkg/services/ngalert/remote/alertmanager.go
+++ b/pkg/services/ngalert/remote/alertmanager.go
@@ -47,6 +47,7 @@ import (
type stateStore interface {
GetSilences(ctx context.Context) (string, error)
GetNotificationLog(ctx context.Context) (string, error)
+ GetFlushLog(ctx context.Context) (string, error)
}
// AutogenFn is a function that adds auto-generated routes to a configuration.
@@ -86,6 +87,8 @@ type Alertmanager struct {
promoteConfig bool
externalURL string
+
+ runtimeConfig remoteClient.RuntimeConfig
}
type AlertmanagerConfig struct {
@@ -111,6 +114,9 @@ type AlertmanagerConfig struct {
// Timeout for the HTTP client.
Timeout time.Duration
+
+ // RuntimeConfig specifies runtime behavior settings for the remote Alertmanager.
+ RuntimeConfig remoteClient.RuntimeConfig
}
func (cfg *AlertmanagerConfig) Validate() error {
@@ -203,6 +209,7 @@ func NewAlertmanager(ctx context.Context, cfg AlertmanagerConfig, store stateSto
externalURL: cfg.ExternalURL,
promoteConfig: cfg.PromoteConfig,
smtp: cfg.SmtpConfig,
+ runtimeConfig: cfg.RuntimeConfig,
}
// Parse the default configuration once and remember its hash so we can compare it later.
@@ -331,10 +338,11 @@ func (am *Alertmanager) buildConfiguration(ctx context.Context, raw []byte, crea
AlertmanagerConfig: mergeResult.Config,
Templates: templates,
},
- CreatedAt: createdAtEpoch,
- Promoted: am.promoteConfig,
- ExternalURL: am.externalURL,
- SmtpConfig: am.smtp,
+ CreatedAt: createdAtEpoch,
+ Promoted: am.promoteConfig,
+ ExternalURL: am.externalURL,
+ SmtpConfig: am.smtp,
+ RuntimeConfig: am.runtimeConfig,
}
cfgHash, err := calculateUserGrafanaConfigHash(payload)
@@ -388,6 +396,8 @@ func (am *Alertmanager) GetRemoteState(ctx context.Context) (notifier.ExternalSt
rs.Silences = p.Data
case "nfl":
rs.Nflog = p.Data
+ case "fls":
+ rs.FlushLog = p.Data
default:
return rs, fmt.Errorf("unknown part key %q", p.Key)
}
@@ -677,6 +687,12 @@ func (am *Alertmanager) getFullState(ctx context.Context) (string, error) {
}
parts = append(parts, alertingClusterPB.Part{Key: notifier.NotificationLogFilename, Data: []byte(notificationLog)})
+ flushLog, err := am.state.GetFlushLog(ctx)
+ if err != nil {
+ return "", fmt.Errorf("error getting flush log: %w", err)
+ }
+ parts = append(parts, alertingClusterPB.Part{Key: notifier.FlushLogFilename, Data: []byte(flushLog)})
+
fs := alertingClusterPB.FullState{
Parts: parts,
}
diff --git a/pkg/services/ngalert/remote/client/alertmanager_configuration.go b/pkg/services/ngalert/remote/client/alertmanager_configuration.go
index a53132a8812..75246a6f32d 100644
--- a/pkg/services/ngalert/remote/client/alertmanager_configuration.go
+++ b/pkg/services/ngalert/remote/client/alertmanager_configuration.go
@@ -29,6 +29,10 @@ func (u *GrafanaAlertmanagerConfig) MarshalJSON() ([]byte, error) {
return definition.MarshalJSONWithSecrets((*cfg)(u))
}
+type RuntimeConfig struct {
+ DispatchTimer string `json:"dispatch_timer"`
+}
+
type UserGrafanaConfig struct {
GrafanaAlertmanagerConfig GrafanaAlertmanagerConfig `json:"configuration"`
Hash string `json:"configuration_hash"`
@@ -37,6 +41,7 @@ type UserGrafanaConfig struct {
Promoted bool `json:"promoted"`
ExternalURL string `json:"external_url"`
SmtpConfig SmtpConfig `json:"smtp_config"`
+ RuntimeConfig RuntimeConfig `json:"runtime_config"`
}
func (mc *Mimir) GetGrafanaAlertmanagerConfig(ctx context.Context) (*UserGrafanaConfig, error) {
diff --git a/pkg/services/preference/generate_themes.go b/pkg/services/preference/generate_themes.go
new file mode 100644
index 00000000000..464e26dbea2
--- /dev/null
+++ b/pkg/services/preference/generate_themes.go
@@ -0,0 +1,90 @@
+//go:build ignore
+
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+type Colors struct {
+ Mode string `json:"mode"`
+}
+
+type ThemeDefinition struct {
+ Colors Colors `json:"colors"`
+ Id string `json:"id"`
+}
+
+func main() {
+ themesPath := filepath.Join("..", "..", "..", "packages", "grafana-data", "src", "themes", "themeDefinitions")
+
+ // Check if the themes directory exists
+ if _, err := os.Stat(themesPath); os.IsNotExist(err) {
+ fmt.Fprintf(os.Stderr, "Themes directory not found: %s\n", themesPath)
+ os.Exit(1)
+ }
+
+ output := `// Code generated by go generate; DO NOT EDIT.
+
+package pref
+
+var themes = []ThemeDTO{
+ {ID: "light", Type: "light"},
+ {ID: "dark", Type: "dark"},
+ {ID: "system", Type: "dark"},
+`
+
+ err := filepath.WalkDir(themesPath, func(path string, d os.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+
+ // Only process json files
+ if d.IsDir() || !strings.HasSuffix(d.Name(), ".json") {
+ return nil
+ }
+
+ fileBytes, readErr := os.ReadFile(path)
+ if readErr != nil {
+ fmt.Fprintf(os.Stderr, "Error reading file %s: %v\n", path, readErr)
+ return nil // Continue processing other files
+ }
+
+ var themeDef ThemeDefinition
+ jsonErr := json.Unmarshal(fileBytes, &themeDef)
+ if jsonErr != nil {
+ fmt.Fprintf(os.Stderr, "Error parsing JSON from %s: %v\n", path, jsonErr)
+ return nil // Continue processing other files
+ }
+
+ themeId := themeDef.Id
+ themeType := "dark" // default fallback
+ if themeDef.Colors.Mode != "" {
+ themeType = themeDef.Colors.Mode
+ }
+
+ output += fmt.Sprintf("\t{ID: %q, Type: %q, IsExtra: true},\n", themeId, themeType)
+
+ return nil
+ })
+
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error walking themes directory: %v\n", err)
+ os.Exit(1)
+ }
+
+ output += "}\n"
+
+ // Write the generated file
+ outputPath := filepath.Join("themes_generated.go")
+ if err := os.WriteFile(outputPath, []byte(output), 0644); err != nil {
+ fmt.Fprintf(os.Stderr, "Error writing output file: %v\n", err)
+ os.Exit(1)
+ }
+
+ fmt.Printf("Successfully generated themes_generated.go\n")
+}
diff --git a/pkg/services/preference/themes.go b/pkg/services/preference/themes.go
index 73366b64d6d..e7164921ccd 100644
--- a/pkg/services/preference/themes.go
+++ b/pkg/services/preference/themes.go
@@ -1,3 +1,5 @@
+//go:generate go run generate_themes.go
+
package pref
type ThemeDTO struct {
@@ -6,24 +8,6 @@ type ThemeDTO struct {
IsExtra bool `json:"isExtra"`
}
-var themes = []ThemeDTO{
- {ID: "light", Type: "light"},
- {ID: "dark", Type: "dark"},
- {ID: "system", Type: "dark"},
- {ID: "debug", Type: "dark", IsExtra: true},
- {ID: "aubergine", Type: "dark", IsExtra: true},
- {ID: "desertbloom", Type: "light", IsExtra: true},
- {ID: "gildedgrove", Type: "dark", IsExtra: true},
- {ID: "mars", Type: "dark", IsExtra: true},
- {ID: "matrix", Type: "dark", IsExtra: true},
- {ID: "sapphiredusk", Type: "dark", IsExtra: true},
- {ID: "synthwave", Type: "dark", IsExtra: true},
- {ID: "tron", Type: "dark", IsExtra: true},
- {ID: "victorian", Type: "dark", IsExtra: true},
- {ID: "zen", Type: "light", IsExtra: true},
- {ID: "gloom", Type: "dark", IsExtra: true},
-}
-
func GetThemeByID(id string) *ThemeDTO {
for _, theme := range themes {
if theme.ID == id {
diff --git a/pkg/services/preference/themes_generated.go b/pkg/services/preference/themes_generated.go
new file mode 100644
index 00000000000..ff09e0f4935
--- /dev/null
+++ b/pkg/services/preference/themes_generated.go
@@ -0,0 +1,21 @@
+// Code generated by go generate; DO NOT EDIT.
+
+package pref
+
+var themes = []ThemeDTO{
+ {ID: "light", Type: "light"},
+ {ID: "dark", Type: "dark"},
+ {ID: "system", Type: "dark"},
+ {ID: "aubergine", Type: "dark", IsExtra: true},
+ {ID: "debug", Type: "dark", IsExtra: true},
+ {ID: "desertbloom", Type: "light", IsExtra: true},
+ {ID: "gildedgrove", Type: "dark", IsExtra: true},
+ {ID: "gloom", Type: "dark", IsExtra: true},
+ {ID: "mars", Type: "dark", IsExtra: true},
+ {ID: "matrix", Type: "dark", IsExtra: true},
+ {ID: "sapphiredusk", Type: "dark", IsExtra: true},
+ {ID: "synthwave", Type: "dark", IsExtra: true},
+ {ID: "tron", Type: "dark", IsExtra: true},
+ {ID: "victorian", Type: "dark", IsExtra: true},
+ {ID: "zen", Type: "light", IsExtra: true},
+}
diff --git a/pkg/services/updatemanager/plugins.go b/pkg/services/updatemanager/plugins.go
index 7ee11b261f9..815e9e67779 100644
--- a/pkg/services/updatemanager/plugins.go
+++ b/pkg/services/updatemanager/plugins.go
@@ -13,6 +13,8 @@ import (
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
+ "github.com/grafana/grafana/pkg/apimachinery/identity"
+ "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/open-feature/go-sdk/openfeature"
"go.opentelemetry.io/otel/codes"
@@ -47,6 +49,7 @@ type PluginsService struct {
updateStrategy string
features featuremgmt.FeatureToggles
+ cfg *setting.Cfg
}
func ProvidePluginsService(cfg *setting.Cfg,
@@ -89,6 +92,7 @@ func ProvidePluginsService(cfg *setting.Cfg,
features: features,
updateChecker: updateChecker,
updateStrategy: cfg.PluginUpdateStrategy,
+ cfg: cfg,
}, nil
}
@@ -136,7 +140,7 @@ func (s *PluginsService) HasUpdate(ctx context.Context, pluginID string) (string
// checkAndUpdate checks for updates and applies them if auto-update is enabled.
func (s *PluginsService) checkAndUpdate(ctx context.Context) {
s.instrumentedCheckForUpdates(ctx)
- if openfeature.NewDefaultClient().Boolean(ctx, featuremgmt.FlagPluginsAutoUpdate, false, openfeature.TransactionContext(ctx)) {
+ if s.checkFlagPluginsAutoUpdate(ctx) {
s.updateAll(ctx)
}
}
@@ -218,6 +222,17 @@ func (s *PluginsService) checkForUpdates(ctx context.Context) error {
return nil
}
+func (s *PluginsService) checkFlagPluginsAutoUpdate(ctx context.Context) bool {
+ ns := request.GetNamespaceMapper(s.cfg)(1)
+ ctx = identity.WithServiceIdentityForSingleNamespaceContext(ctx, ns)
+ flag, err := openfeature.NewDefaultClient().BooleanValueDetails(ctx, featuremgmt.FlagPluginsAutoUpdate, false, openfeature.TransactionContext(ctx))
+ if err != nil {
+ s.log.Error("flag evaluation error", "flag", featuremgmt.FlagPluginsAutoUpdate, "error", err)
+ }
+
+ return flag.Value
+}
+
func (s *PluginsService) canUpdate(ctx context.Context, plugin pluginstore.Plugin, gcomVersion string) bool {
if !s.updateChecker.IsUpdatable(ctx, plugin) {
return false
@@ -227,7 +242,7 @@ func (s *PluginsService) canUpdate(ctx context.Context, plugin pluginstore.Plugi
return false
}
- if openfeature.NewDefaultClient().Boolean(ctx, featuremgmt.FlagPluginsAutoUpdate, false, openfeature.TransactionContext(ctx)) {
+ if s.checkFlagPluginsAutoUpdate(ctx) {
return s.updateChecker.CanUpdate(plugin.ID, plugin.Info.Version, gcomVersion, s.updateStrategy == setting.PluginUpdateStrategyMinor)
}
diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go
index 9667b82b9fa..1e26b9067ef 100644
--- a/pkg/setting/setting.go
+++ b/pkg/setting/setting.go
@@ -600,6 +600,7 @@ type Cfg struct {
IndexRebuildInterval time.Duration
IndexCacheTTL time.Duration
IndexMinUpdateInterval time.Duration // Don't update index if it was updated less than this interval ago.
+ IndexScoringModel string // Note: Temporary config to switch the index scoring model and will be removed soon.
MaxFileIndexAge time.Duration // Max age of file-based indexes. Index older than this will be rebuilt asynchronously.
MinFileIndexBuildVersion string // Minimum version of Grafana that built the file-based index. If index was built with older Grafana, it will be rebuilt asynchronously.
EnableSharding bool
diff --git a/pkg/setting/setting_unified_storage.go b/pkg/setting/setting_unified_storage.go
index 21a3f455993..b47e8879826 100644
--- a/pkg/setting/setting_unified_storage.go
+++ b/pkg/setting/setting_unified_storage.go
@@ -123,6 +123,10 @@ func (cfg *Cfg) setUnifiedStorageConfig() {
cfg.IndexRebuildInterval = section.Key("index_rebuild_interval").MustDuration(24 * time.Hour)
cfg.IndexCacheTTL = section.Key("index_cache_ttl").MustDuration(10 * time.Minute)
cfg.IndexMinUpdateInterval = section.Key("index_min_update_interval").MustDuration(0)
+ cfg.IndexScoringModel = section.Key("index_scoring_model").MustString("")
+ if cfg.IndexScoringModel != "" {
+ cfg.Logger.Info("Index scoring model set", "model", cfg.IndexScoringModel)
+ }
cfg.SprinklesApiServer = section.Key("sprinkles_api_server").String()
cfg.SprinklesApiServerPageLimit = section.Key("sprinkles_api_server_page_limit").MustInt(10000)
cfg.CACertPath = section.Key("ca_cert_path").String()
diff --git a/pkg/storage/unified/README.md b/pkg/storage/unified/README.md
index 9cd0d1fd01d..5aadb0ad2b0 100644
--- a/pkg/storage/unified/README.md
+++ b/pkg/storage/unified/README.md
@@ -237,7 +237,6 @@ kubernetesFolders = true
unifiedStorage = true
unifiedStorageHistoryPruner = true
unifiedStorageSearchPermissionFiltering = false
-unifiedStorageSearchSprinkles = false
[unified_storage]
enable_search = true
@@ -315,9 +314,6 @@ To enable it, add the following to your `custom.ini` under the `[feature_toggles
; Used by the Grafana instance
unifiedStorageSearchUI = true
-; (optional) Allows you to sort dashboards by usage insights fields when using enterprise
-; unifiedStorageSearchSprinkles = true
-
[unified_storage]
; Used by unified storage server
enable_search = true
@@ -934,7 +930,6 @@ Unified Search requires several feature flags to be enabled depending on the des
| Feature Flag | Purpose | Stage | Required For |
|--------------|---------|-------|--------------|
| `unifiedStorageSearchUI` | Frontend search interface | Experimental | Grafana UI search |
-| `unifiedStorageSearchSprinkles` | Usage insights integration | Experimental | Dashboard usage sorting (Enterprise) |
| `unifiedStorageSearchDualReaderEnabled` | Shadow traffic to unified search | Experimental | Shadow traffic during migration |
#### Unified Search Specific Configuration
@@ -955,9 +950,6 @@ unifiedStorageSearchUI = true
; Enable shadow traffic during migration (optional)
unifiedStorageSearchDualReaderEnabled = true
-; Enable usage insights sorting (Enterprise only)
-unifiedStorageSearchSprinkles = true
-
[unified_storage]
; Enable core search functionality (required)
enable_search = true
diff --git a/pkg/storage/unified/client.go b/pkg/storage/unified/client.go
index 82336b3a5d1..e5c396907e3 100644
--- a/pkg/storage/unified/client.go
+++ b/pkg/storage/unified/client.go
@@ -271,7 +271,7 @@ func grpcConn(address string, metrics *clientMetrics, clientKeepaliveTime time.D
retryCfg := retryConfig{
Max: 3,
Backoff: time.Second,
- BackoffJitter: 0.5,
+ BackoffJitter: 0.1,
}
unary = append(unary, unaryRetryInterceptor(retryCfg))
unary = append(unary, unaryRetryInstrument(metrics.requestRetries))
@@ -288,13 +288,15 @@ func grpcConn(address string, metrics *clientMetrics, clientKeepaliveTime time.D
opts = append(opts, grpc.WithStatsHandler(otelgrpc.NewClientHandler()))
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
- // Use round_robin to balances requests more evenly over the available Storage server.
+ // Use round_robin to balance requests more evenly over the available Storage server.
opts = append(opts, grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`))
// Disable looking up service config from TXT DNS records.
// This reduces the number of requests made to the DNS servers.
opts = append(opts, grpc.WithDisableServiceConfig())
+ opts = append(opts, connectionBackoffOptions())
+
if clientKeepaliveTime > 0 {
opts = append(opts, grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: clientKeepaliveTime,
diff --git a/pkg/storage/unified/client_retry.go b/pkg/storage/unified/client_retry.go
index 47b899e4198..3df79807673 100644
--- a/pkg/storage/unified/client_retry.go
+++ b/pkg/storage/unified/client_retry.go
@@ -9,6 +9,7 @@ import (
"github.com/grpc-ecosystem/go-grpc-middleware/util/metautils"
"github.com/prometheus/client_golang/prometheus"
"google.golang.org/grpc"
+ "google.golang.org/grpc/backoff"
"google.golang.org/grpc/codes"
)
@@ -44,3 +45,17 @@ func unaryRetryInstrument(metric *prometheus.CounterVec) grpc.UnaryClientInterce
return invoker(ctx, method, req, resp, cc, opts...)
}
}
+
+// connectionBackoffOptions configures connection backoff parameters for faster recovery from
+// transient connection failures (e.g., during pod restarts).
+func connectionBackoffOptions() grpc.DialOption {
+ return grpc.WithConnectParams(grpc.ConnectParams{
+ Backoff: backoff.Config{
+ BaseDelay: 100 * time.Millisecond,
+ Multiplier: 1.6,
+ Jitter: 0.2,
+ MaxDelay: 10 * time.Second,
+ },
+ MinConnectTimeout: 5 * time.Second,
+ })
+}
diff --git a/pkg/storage/unified/proto/search.proto b/pkg/storage/unified/proto/search.proto
index 5018c97c9db..62c6afb323a 100644
--- a/pkg/storage/unified/proto/search.proto
+++ b/pkg/storage/unified/proto/search.proto
@@ -9,11 +9,13 @@ import "resource.proto";
// Unlike the ResourceStore, this service can be exposed to clients directly
// It should be implemented with efficient indexes and does not need read-after-write semantics
service ResourceIndex {
+ // Query for documents
rpc Search(ResourceSearchRequest) returns (ResourceSearchResponse);
// Get the resource stats
rpc GetStats(ResourceStatsRequest) returns (ResourceStatsResponse);
+ // Rebuild the search index
rpc RebuildIndexes(RebuildIndexesRequest) returns (RebuildIndexesResponse);
}
@@ -49,6 +51,20 @@ message ResourceStatsResponse {
repeated Stats stats = 2;
}
+// This controls what query and analyzers are applied to the specified field
+// See: https://blevesearch.com/docs/Analyzers/
+enum QueryFieldType {
+ // Picks a reasonable analyzer given the input. Currently this always uses TEXT
+ // In the future, it may change to depend on the indexed field type
+ DEFAULT = 0;
+ // Use free text analyzer. The query is broken into a normalized set of tokens
+ TEXT = 1;
+ // The query must exactly match the indexed token
+ KEYWORD = 2;
+ // Like a text query, but the position and offsets influence the score
+ PHRASE = 3;
+}
+
// Search within a single resource
message ResourceSearchRequest {
message Sort {
@@ -64,6 +80,18 @@ message ResourceSearchRequest {
// date queries
}
+ // Defines the field in the index to query
+ // Boost is optional, and allows weighting the field higher in the results
+ message QueryField {
+ // The field name in the index to query
+ string name = 1;
+
+ QueryFieldType type = 2;
+
+ // Boost value for this field
+ float boost = 3;
+ }
+
// The key must include namespace + group + resource
ListOptions options = 1;
@@ -99,6 +127,9 @@ message ResourceSearchRequest {
int64 page = 11;
int64 permission = 12;
+
+ // Optionally specify which fields are included in the query
+ repeated QueryField query_fields = 13;
}
message ResourceSearchResponse {
diff --git a/pkg/storage/unified/resource/document.go b/pkg/storage/unified/resource/document.go
index 4e528b96df0..6a41689c0da 100644
--- a/pkg/storage/unified/resource/document.go
+++ b/pkg/storage/unified/resource/document.go
@@ -290,7 +290,6 @@ const SEARCH_FIELD_NAMESPACE = "namespace"
const SEARCH_FIELD_NAME = "name"
const SEARCH_FIELD_RV = "rv"
const SEARCH_FIELD_TITLE = "title"
-const SEARCH_FIELD_TITLE_NGRAM = "title_ngram"
const SEARCH_FIELD_TITLE_PHRASE = "title_phrase" // filtering/sorting on title by full phrase
const SEARCH_FIELD_DESCRIPTION = "description"
const SEARCH_FIELD_TAGS = "tags"
diff --git a/pkg/storage/unified/resourcepb/search.pb.go b/pkg/storage/unified/resourcepb/search.pb.go
index 459e9aa3429..e523c112093 100644
--- a/pkg/storage/unified/resourcepb/search.pb.go
+++ b/pkg/storage/unified/resourcepb/search.pb.go
@@ -21,6 +21,65 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
+// This controls what query and analyzers are applied to the specified field
+// See: https://blevesearch.com/docs/Analyzers/
+type QueryFieldType int32
+
+const (
+ // Picks a reasonable analyzer given the input. Currently this always uses TEXT
+ // In the future, it may change to depend on the indexed field type
+ QueryFieldType_DEFAULT QueryFieldType = 0
+ // Use free text analyzer. The query is broken into a normalized set of tokens
+ QueryFieldType_TEXT QueryFieldType = 1
+ // The query must exactly match the indexed token
+ QueryFieldType_KEYWORD QueryFieldType = 2
+ // Like a text query, but the position and offsets influence the score
+ QueryFieldType_PHRASE QueryFieldType = 3
+)
+
+// Enum value maps for QueryFieldType.
+var (
+ QueryFieldType_name = map[int32]string{
+ 0: "DEFAULT",
+ 1: "TEXT",
+ 2: "KEYWORD",
+ 3: "PHRASE",
+ }
+ QueryFieldType_value = map[string]int32{
+ "DEFAULT": 0,
+ "TEXT": 1,
+ "KEYWORD": 2,
+ "PHRASE": 3,
+ }
+)
+
+func (x QueryFieldType) Enum() *QueryFieldType {
+ p := new(QueryFieldType)
+ *p = x
+ return p
+}
+
+func (x QueryFieldType) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (QueryFieldType) Descriptor() protoreflect.EnumDescriptor {
+ return file_search_proto_enumTypes[0].Descriptor()
+}
+
+func (QueryFieldType) Type() protoreflect.EnumType {
+ return &file_search_proto_enumTypes[0]
+}
+
+func (x QueryFieldType) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use QueryFieldType.Descriptor instead.
+func (QueryFieldType) EnumDescriptor() ([]byte, []int) {
+ return file_search_proto_rawDescGZIP(), []int{0}
+}
+
// Get statistics across multiple resources
// For these queries, we do not need authorization to see the actual values
type ResourceStatsRequest struct {
@@ -165,10 +224,12 @@ type ResourceSearchRequest struct {
// the return fields (empty will return everything)
Fields []string `protobuf:"bytes,8,rep,name=fields,proto3" json:"fields,omitempty"`
// explain each result (added to the each row)
- Explain bool `protobuf:"varint,9,opt,name=explain,proto3" json:"explain,omitempty"`
- IsDeleted bool `protobuf:"varint,10,opt,name=is_deleted,json=isDeleted,proto3" json:"is_deleted,omitempty"`
- Page int64 `protobuf:"varint,11,opt,name=page,proto3" json:"page,omitempty"`
- Permission int64 `protobuf:"varint,12,opt,name=permission,proto3" json:"permission,omitempty"`
+ Explain bool `protobuf:"varint,9,opt,name=explain,proto3" json:"explain,omitempty"`
+ IsDeleted bool `protobuf:"varint,10,opt,name=is_deleted,json=isDeleted,proto3" json:"is_deleted,omitempty"`
+ Page int64 `protobuf:"varint,11,opt,name=page,proto3" json:"page,omitempty"`
+ Permission int64 `protobuf:"varint,12,opt,name=permission,proto3" json:"permission,omitempty"`
+ // Optionally specify which fields are included in the query
+ QueryFields []*ResourceSearchRequest_QueryField `protobuf:"bytes,13,rep,name=query_fields,json=queryFields,proto3" json:"query_fields,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -287,6 +348,13 @@ func (x *ResourceSearchRequest) GetPermission() int64 {
return 0
}
+func (x *ResourceSearchRequest) GetQueryFields() []*ResourceSearchRequest_QueryField {
+ if x != nil {
+ return x.QueryFields
+ }
+ return nil
+}
+
type ResourceSearchResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Error details
@@ -670,6 +738,70 @@ func (x *ResourceSearchRequest_Facet) GetLimit() int64 {
return 0
}
+// Defines the field in the index to query
+// Boost is optional, and allows weighting the field higher in the results
+type ResourceSearchRequest_QueryField struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ // The field name in the index to query
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ Type QueryFieldType `protobuf:"varint,2,opt,name=type,proto3,enum=resource.QueryFieldType" json:"type,omitempty"`
+ // Boost value for this field
+ Boost float32 `protobuf:"fixed32,3,opt,name=boost,proto3" json:"boost,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *ResourceSearchRequest_QueryField) Reset() {
+ *x = ResourceSearchRequest_QueryField{}
+ mi := &file_search_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *ResourceSearchRequest_QueryField) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ResourceSearchRequest_QueryField) ProtoMessage() {}
+
+func (x *ResourceSearchRequest_QueryField) ProtoReflect() protoreflect.Message {
+ mi := &file_search_proto_msgTypes[9]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ResourceSearchRequest_QueryField.ProtoReflect.Descriptor instead.
+func (*ResourceSearchRequest_QueryField) Descriptor() ([]byte, []int) {
+ return file_search_proto_rawDescGZIP(), []int{2, 2}
+}
+
+func (x *ResourceSearchRequest_QueryField) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *ResourceSearchRequest_QueryField) GetType() QueryFieldType {
+ if x != nil {
+ return x.Type
+ }
+ return QueryFieldType_DEFAULT
+}
+
+func (x *ResourceSearchRequest_QueryField) GetBoost() float32 {
+ if x != nil {
+ return x.Boost
+ }
+ return 0
+}
+
type ResourceSearchResponse_Facet struct {
state protoimpl.MessageState `protogen:"open.v1"`
Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
@@ -685,7 +817,7 @@ type ResourceSearchResponse_Facet struct {
func (x *ResourceSearchResponse_Facet) Reset() {
*x = ResourceSearchResponse_Facet{}
- mi := &file_search_proto_msgTypes[10]
+ mi := &file_search_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -697,7 +829,7 @@ func (x *ResourceSearchResponse_Facet) String() string {
func (*ResourceSearchResponse_Facet) ProtoMessage() {}
func (x *ResourceSearchResponse_Facet) ProtoReflect() protoreflect.Message {
- mi := &file_search_proto_msgTypes[10]
+ mi := &file_search_proto_msgTypes[11]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -751,7 +883,7 @@ type ResourceSearchResponse_TermFacet struct {
func (x *ResourceSearchResponse_TermFacet) Reset() {
*x = ResourceSearchResponse_TermFacet{}
- mi := &file_search_proto_msgTypes[11]
+ mi := &file_search_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -763,7 +895,7 @@ func (x *ResourceSearchResponse_TermFacet) String() string {
func (*ResourceSearchResponse_TermFacet) ProtoMessage() {}
func (x *ResourceSearchResponse_TermFacet) ProtoReflect() protoreflect.Message {
- mi := &file_search_proto_msgTypes[11]
+ mi := &file_search_proto_msgTypes[12]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -818,7 +950,7 @@ var file_search_proto_rawDesc = string([]byte{
0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e,
- 0x74, 0x22, 0x8e, 0x05, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65,
+ 0x74, 0x22, 0xc3, 0x06, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65,
0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x07, 0x6f,
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x70, 0x74, 0x69,
@@ -846,93 +978,109 @@ var file_search_proto_rawDesc = string([]byte{
0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x0b,
0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65,
0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a,
- 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x30, 0x0a, 0x04, 0x53, 0x6f,
- 0x72, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x1a, 0x33, 0x0a, 0x05,
- 0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c,
- 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69,
- 0x74, 0x1a, 0x5f, 0x0a, 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
- 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
- 0x79, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
- 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
+ 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x0c, 0x71, 0x75,
+ 0x65, 0x72, 0x79, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
- 0x38, 0x01, 0x22, 0xea, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
- 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a,
- 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72,
- 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73,
- 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65,
- 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
- 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x03,
- 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x03,
- 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
- 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x07, 0x72,
- 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
- 0x68, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x6f, 0x74, 0x61,
- 0x6c, 0x48, 0x69, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x63,
- 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79,
- 0x43, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x63, 0x6f, 0x72,
- 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x53, 0x63, 0x6f, 0x72,
- 0x65, 0x12, 0x41, 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b,
- 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f,
+ 0x74, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x0b, 0x71, 0x75,
+ 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x30, 0x0a, 0x04, 0x53, 0x6f, 0x72,
+ 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x1a, 0x33, 0x0a, 0x05, 0x46,
+ 0x61, 0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69,
+ 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74,
+ 0x1a, 0x64, 0x0a, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x12,
+ 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e,
+ 0x32, 0x18, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x51, 0x75, 0x65, 0x72,
+ 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
+ 0x12, 0x14, 0x0a, 0x05, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52,
+ 0x05, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x1a, 0x5f, 0x0a, 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45,
+ 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xea, 0x04, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
- 0x73, 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x66,
- 0x61, 0x63, 0x65, 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x05, 0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x14,
- 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66,
- 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69,
- 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x69, 0x73,
- 0x73, 0x69, 0x6e, 0x67, 0x12, 0x40, 0x0a, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x18, 0x04, 0x20,
- 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52,
- 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73,
- 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52,
- 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x1a, 0x35, 0x0a, 0x09, 0x54, 0x65, 0x72, 0x6d, 0x46, 0x61,
- 0x63, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74,
- 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x60, 0x0a,
- 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
- 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3c, 0x0a,
- 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x72,
+ 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72,
+ 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12,
+ 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
- 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46,
- 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22,
- 0x60, 0x0a, 0x15, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65,
- 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65,
- 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d,
- 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02,
- 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
- 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x04, 0x6b, 0x65, 0x79,
- 0x73, 0x22, 0x83, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64,
- 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c,
- 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x03, 0x52, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74,
- 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2b, 0x0a, 0x05, 0x65, 0x72,
- 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f,
- 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74,
- 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x32, 0xfe, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f,
- 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x4b, 0x0a, 0x06, 0x53, 0x65, 0x61,
- 0x72, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52,
- 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e,
- 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65,
- 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61,
- 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65,
- 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65,
- 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e,
- 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
- 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
- 0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68,
- 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x67,
- 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61,
- 0x67, 0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75,
- 0x72, 0x63, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75,
+ 0x6c, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x61, 0x62,
+ 0x6c, 0x65, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74,
+ 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x68, 0x69, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
+ 0x09, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x48, 0x69, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x71, 0x75,
+ 0x65, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09,
+ 0x71, 0x75, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78,
+ 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6d, 0x61,
+ 0x78, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x41, 0x0a, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x18,
+ 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52,
+ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74,
+ 0x72, 0x79, 0x52, 0x05, 0x66, 0x61, 0x63, 0x65, 0x74, 0x1a, 0x8f, 0x01, 0x0a, 0x05, 0x46, 0x61,
+ 0x63, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74,
+ 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12,
+ 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
+ 0x52, 0x07, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x40, 0x0a, 0x05, 0x74, 0x65, 0x72,
+ 0x6d, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75,
+ 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72,
+ 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x54, 0x65, 0x72, 0x6d, 0x46,
+ 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x1a, 0x35, 0x0a, 0x09, 0x54,
+ 0x65, 0x72, 0x6d, 0x46, 0x61, 0x63, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x14, 0x0a, 0x05,
+ 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75,
+ 0x6e, 0x74, 0x1a, 0x60, 0x0a, 0x0a, 0x46, 0x61, 0x63, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
+ 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
+ 0x65, 0x79, 0x12, 0x3c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x26, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x61, 0x63, 0x65, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x3a, 0x02, 0x38, 0x01, 0x22, 0x60, 0x0a, 0x15, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49,
+ 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a,
+ 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x6b,
+ 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x72, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x65, 0x79,
+ 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x83, 0x01, 0x0a, 0x16, 0x52, 0x65, 0x62, 0x75, 0x69,
+ 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x75, 0x6e,
+ 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64,
+ 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12,
+ 0x2b, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15,
+ 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52,
+ 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2a, 0x40, 0x0a, 0x0e,
+ 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b,
+ 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x54,
+ 0x45, 0x58, 0x54, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x4b, 0x45, 0x59, 0x57, 0x4f, 0x52, 0x44,
+ 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x48, 0x52, 0x41, 0x53, 0x45, 0x10, 0x03, 0x32, 0xfe,
+ 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78,
+ 0x12, 0x4b, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65,
+ 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53,
+ 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a,
+ 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x72, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61,
+ 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x74, 0x61,
+ 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0e, 0x52, 0x65,
+ 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x12, 0x1f, 0x2e, 0x72,
+ 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49,
+ 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e,
+ 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64,
+ 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42,
+ 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72,
+ 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x6b,
+ 0x67, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2f, 0x75, 0x6e, 0x69, 0x66, 0x69, 0x65,
+ 0x64, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x33,
})
var (
@@ -947,53 +1095,58 @@ func file_search_proto_rawDescGZIP() []byte {
return file_search_proto_rawDescData
}
-var file_search_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
+var file_search_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_search_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
var file_search_proto_goTypes = []any{
- (*ResourceStatsRequest)(nil), // 0: resource.ResourceStatsRequest
- (*ResourceStatsResponse)(nil), // 1: resource.ResourceStatsResponse
- (*ResourceSearchRequest)(nil), // 2: resource.ResourceSearchRequest
- (*ResourceSearchResponse)(nil), // 3: resource.ResourceSearchResponse
- (*RebuildIndexesRequest)(nil), // 4: resource.RebuildIndexesRequest
- (*RebuildIndexesResponse)(nil), // 5: resource.RebuildIndexesResponse
- (*ResourceStatsResponse_Stats)(nil), // 6: resource.ResourceStatsResponse.Stats
- (*ResourceSearchRequest_Sort)(nil), // 7: resource.ResourceSearchRequest.Sort
- (*ResourceSearchRequest_Facet)(nil), // 8: resource.ResourceSearchRequest.Facet
- nil, // 9: resource.ResourceSearchRequest.FacetEntry
- (*ResourceSearchResponse_Facet)(nil), // 10: resource.ResourceSearchResponse.Facet
- (*ResourceSearchResponse_TermFacet)(nil), // 11: resource.ResourceSearchResponse.TermFacet
- nil, // 12: resource.ResourceSearchResponse.FacetEntry
- (*ErrorResult)(nil), // 13: resource.ErrorResult
- (*ListOptions)(nil), // 14: resource.ListOptions
- (*ResourceKey)(nil), // 15: resource.ResourceKey
- (*ResourceTable)(nil), // 16: resource.ResourceTable
+ (QueryFieldType)(0), // 0: resource.QueryFieldType
+ (*ResourceStatsRequest)(nil), // 1: resource.ResourceStatsRequest
+ (*ResourceStatsResponse)(nil), // 2: resource.ResourceStatsResponse
+ (*ResourceSearchRequest)(nil), // 3: resource.ResourceSearchRequest
+ (*ResourceSearchResponse)(nil), // 4: resource.ResourceSearchResponse
+ (*RebuildIndexesRequest)(nil), // 5: resource.RebuildIndexesRequest
+ (*RebuildIndexesResponse)(nil), // 6: resource.RebuildIndexesResponse
+ (*ResourceStatsResponse_Stats)(nil), // 7: resource.ResourceStatsResponse.Stats
+ (*ResourceSearchRequest_Sort)(nil), // 8: resource.ResourceSearchRequest.Sort
+ (*ResourceSearchRequest_Facet)(nil), // 9: resource.ResourceSearchRequest.Facet
+ (*ResourceSearchRequest_QueryField)(nil), // 10: resource.ResourceSearchRequest.QueryField
+ nil, // 11: resource.ResourceSearchRequest.FacetEntry
+ (*ResourceSearchResponse_Facet)(nil), // 12: resource.ResourceSearchResponse.Facet
+ (*ResourceSearchResponse_TermFacet)(nil), // 13: resource.ResourceSearchResponse.TermFacet
+ nil, // 14: resource.ResourceSearchResponse.FacetEntry
+ (*ErrorResult)(nil), // 15: resource.ErrorResult
+ (*ListOptions)(nil), // 16: resource.ListOptions
+ (*ResourceKey)(nil), // 17: resource.ResourceKey
+ (*ResourceTable)(nil), // 18: resource.ResourceTable
}
var file_search_proto_depIdxs = []int32{
- 13, // 0: resource.ResourceStatsResponse.error:type_name -> resource.ErrorResult
- 6, // 1: resource.ResourceStatsResponse.stats:type_name -> resource.ResourceStatsResponse.Stats
- 14, // 2: resource.ResourceSearchRequest.options:type_name -> resource.ListOptions
- 15, // 3: resource.ResourceSearchRequest.federated:type_name -> resource.ResourceKey
- 7, // 4: resource.ResourceSearchRequest.sortBy:type_name -> resource.ResourceSearchRequest.Sort
- 9, // 5: resource.ResourceSearchRequest.facet:type_name -> resource.ResourceSearchRequest.FacetEntry
- 13, // 6: resource.ResourceSearchResponse.error:type_name -> resource.ErrorResult
- 15, // 7: resource.ResourceSearchResponse.key:type_name -> resource.ResourceKey
- 16, // 8: resource.ResourceSearchResponse.results:type_name -> resource.ResourceTable
- 12, // 9: resource.ResourceSearchResponse.facet:type_name -> resource.ResourceSearchResponse.FacetEntry
- 15, // 10: resource.RebuildIndexesRequest.keys:type_name -> resource.ResourceKey
- 13, // 11: resource.RebuildIndexesResponse.error:type_name -> resource.ErrorResult
- 8, // 12: resource.ResourceSearchRequest.FacetEntry.value:type_name -> resource.ResourceSearchRequest.Facet
- 11, // 13: resource.ResourceSearchResponse.Facet.terms:type_name -> resource.ResourceSearchResponse.TermFacet
- 10, // 14: resource.ResourceSearchResponse.FacetEntry.value:type_name -> resource.ResourceSearchResponse.Facet
- 2, // 15: resource.ResourceIndex.Search:input_type -> resource.ResourceSearchRequest
- 0, // 16: resource.ResourceIndex.GetStats:input_type -> resource.ResourceStatsRequest
- 4, // 17: resource.ResourceIndex.RebuildIndexes:input_type -> resource.RebuildIndexesRequest
- 3, // 18: resource.ResourceIndex.Search:output_type -> resource.ResourceSearchResponse
- 1, // 19: resource.ResourceIndex.GetStats:output_type -> resource.ResourceStatsResponse
- 5, // 20: resource.ResourceIndex.RebuildIndexes:output_type -> resource.RebuildIndexesResponse
- 18, // [18:21] is the sub-list for method output_type
- 15, // [15:18] is the sub-list for method input_type
- 15, // [15:15] is the sub-list for extension type_name
- 15, // [15:15] is the sub-list for extension extendee
- 0, // [0:15] is the sub-list for field type_name
+ 15, // 0: resource.ResourceStatsResponse.error:type_name -> resource.ErrorResult
+ 7, // 1: resource.ResourceStatsResponse.stats:type_name -> resource.ResourceStatsResponse.Stats
+ 16, // 2: resource.ResourceSearchRequest.options:type_name -> resource.ListOptions
+ 17, // 3: resource.ResourceSearchRequest.federated:type_name -> resource.ResourceKey
+ 8, // 4: resource.ResourceSearchRequest.sortBy:type_name -> resource.ResourceSearchRequest.Sort
+ 11, // 5: resource.ResourceSearchRequest.facet:type_name -> resource.ResourceSearchRequest.FacetEntry
+ 10, // 6: resource.ResourceSearchRequest.query_fields:type_name -> resource.ResourceSearchRequest.QueryField
+ 15, // 7: resource.ResourceSearchResponse.error:type_name -> resource.ErrorResult
+ 17, // 8: resource.ResourceSearchResponse.key:type_name -> resource.ResourceKey
+ 18, // 9: resource.ResourceSearchResponse.results:type_name -> resource.ResourceTable
+ 14, // 10: resource.ResourceSearchResponse.facet:type_name -> resource.ResourceSearchResponse.FacetEntry
+ 17, // 11: resource.RebuildIndexesRequest.keys:type_name -> resource.ResourceKey
+ 15, // 12: resource.RebuildIndexesResponse.error:type_name -> resource.ErrorResult
+ 0, // 13: resource.ResourceSearchRequest.QueryField.type:type_name -> resource.QueryFieldType
+ 9, // 14: resource.ResourceSearchRequest.FacetEntry.value:type_name -> resource.ResourceSearchRequest.Facet
+ 13, // 15: resource.ResourceSearchResponse.Facet.terms:type_name -> resource.ResourceSearchResponse.TermFacet
+ 12, // 16: resource.ResourceSearchResponse.FacetEntry.value:type_name -> resource.ResourceSearchResponse.Facet
+ 3, // 17: resource.ResourceIndex.Search:input_type -> resource.ResourceSearchRequest
+ 1, // 18: resource.ResourceIndex.GetStats:input_type -> resource.ResourceStatsRequest
+ 5, // 19: resource.ResourceIndex.RebuildIndexes:input_type -> resource.RebuildIndexesRequest
+ 4, // 20: resource.ResourceIndex.Search:output_type -> resource.ResourceSearchResponse
+ 2, // 21: resource.ResourceIndex.GetStats:output_type -> resource.ResourceStatsResponse
+ 6, // 22: resource.ResourceIndex.RebuildIndexes:output_type -> resource.RebuildIndexesResponse
+ 20, // [20:23] is the sub-list for method output_type
+ 17, // [17:20] is the sub-list for method input_type
+ 17, // [17:17] is the sub-list for extension type_name
+ 17, // [17:17] is the sub-list for extension extendee
+ 0, // [0:17] is the sub-list for field type_name
}
func init() { file_search_proto_init() }
@@ -1007,13 +1160,14 @@ func file_search_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_search_proto_rawDesc), len(file_search_proto_rawDesc)),
- NumEnums: 0,
- NumMessages: 13,
+ NumEnums: 1,
+ NumMessages: 14,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_search_proto_goTypes,
DependencyIndexes: file_search_proto_depIdxs,
+ EnumInfos: file_search_proto_enumTypes,
MessageInfos: file_search_proto_msgTypes,
}.Build()
File_search_proto = out.File
diff --git a/pkg/storage/unified/resourcepb/search_grpc.pb.go b/pkg/storage/unified/resourcepb/search_grpc.pb.go
index d69cbd14e38..d8db878ef55 100644
--- a/pkg/storage/unified/resourcepb/search_grpc.pb.go
+++ b/pkg/storage/unified/resourcepb/search_grpc.pb.go
@@ -31,9 +31,11 @@ const (
// Unlike the ResourceStore, this service can be exposed to clients directly
// It should be implemented with efficient indexes and does not need read-after-write semantics
type ResourceIndexClient interface {
+ // Query for documents
Search(ctx context.Context, in *ResourceSearchRequest, opts ...grpc.CallOption) (*ResourceSearchResponse, error)
// Get the resource stats
GetStats(ctx context.Context, in *ResourceStatsRequest, opts ...grpc.CallOption) (*ResourceStatsResponse, error)
+ // Rebuild the search index
RebuildIndexes(ctx context.Context, in *RebuildIndexesRequest, opts ...grpc.CallOption) (*RebuildIndexesResponse, error)
}
@@ -82,9 +84,11 @@ func (c *resourceIndexClient) RebuildIndexes(ctx context.Context, in *RebuildInd
// Unlike the ResourceStore, this service can be exposed to clients directly
// It should be implemented with efficient indexes and does not need read-after-write semantics
type ResourceIndexServer interface {
+ // Query for documents
Search(context.Context, *ResourceSearchRequest) (*ResourceSearchResponse, error)
// Get the resource stats
GetStats(context.Context, *ResourceStatsRequest) (*ResourceStatsResponse, error)
+ // Rebuild the search index
RebuildIndexes(context.Context, *RebuildIndexesRequest) (*RebuildIndexesResponse, error)
}
diff --git a/pkg/storage/unified/search/bleve.go b/pkg/storage/unified/search/bleve.go
index eec7290633b..09ef2dc9230 100644
--- a/pkg/storage/unified/search/bleve.go
+++ b/pkg/storage/unified/search/bleve.go
@@ -81,6 +81,11 @@ type BleveOptions struct {
// Indexes that are not owned by current instance are eligible for cleanup.
// If nil, all indexes are owned by the current instance.
OwnsIndex func(key resource.NamespacedResource) (bool, error)
+
+ // ScoringModel defines the scoring model used for the bleve indexes
+ // Default: index.TFIDFScoring
+ // Supported values: index.TFIDFScoring and index.BM25Scoring
+ ScoringModel string
}
type bleveBackend struct {
@@ -368,7 +373,7 @@ func (b *bleveBackend) BuildIndex(
attribute.String("reason", indexBuildReason),
)
- mapper, err := GetBleveMappings(fields)
+ mapper, err := GetBleveMappings(b.opts.ScoringModel, fields)
if err != nil {
return nil, err
}
@@ -1177,6 +1182,7 @@ func (b *bleveIndex) getIndex(
return b.index, nil
}
+// nolint:gocyclo
func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resourcepb.ResourceSearchRequest, access authlib.AccessClient) (*bleve.SearchRequest, *resourcepb.ErrorResult) {
ctx, span := tracer.Start(ctx, "search.bleveIndex.toBleveSearchRequest")
defer span.End()
@@ -1235,42 +1241,62 @@ func (b *bleveIndex) toBleveSearchRequest(ctx context.Context, req *resourcepb.R
}
}
- if len(req.Query) > 1 && strings.Contains(req.Query, "*") {
- // wildcard query is expensive - should be used with caution
- wildcard := bleve.NewWildcardQuery(req.Query)
- queries = append(queries, wildcard)
- }
+ if len(req.Query) > 1 {
+ if strings.Contains(req.Query, "*") {
+ // wildcard query is expensive - should be used with caution
+ wildcard := bleve.NewWildcardQuery(req.Query)
+ queries = append(queries, wildcard)
+ } else {
+ // When using a
+ searchrequest.Fields = append(searchrequest.Fields, resource.SEARCH_FIELD_SCORE)
+ disjoin := bleve.NewDisjunctionQuery()
+ queries = append(queries, disjoin)
- if req.Query != "" && !strings.Contains(req.Query, "*") {
- // Add a text query
- searchrequest.Fields = append(searchrequest.Fields, resource.SEARCH_FIELD_SCORE)
+ queryFields := req.QueryFields
+ if len(queryFields) == 0 {
+ queryFields = []*resourcepb.ResourceSearchRequest_QueryField{
+ {
+ Name: resource.SEARCH_FIELD_TITLE,
+ Type: resourcepb.QueryFieldType_KEYWORD,
+ Boost: 10, // exact match -- includes ngrams! If they lived on their own field, we could score them differently
+ }, {
+ Name: resource.SEARCH_FIELD_TITLE,
+ Type: resourcepb.QueryFieldType_TEXT,
+ Boost: 2, // standard analyzer (with ngrams!)
+ }, {
+ Name: resource.SEARCH_FIELD_TITLE_PHRASE,
+ Type: resourcepb.QueryFieldType_TEXT,
+ Boost: 5, // standard analyzer
+ },
+ }
+ }
- // There are multiple ways to match the query string to documents. The following queries are ordered by priority:
+ for _, field := range queryFields {
+ switch field.Type {
+ case resourcepb.QueryFieldType_TEXT, resourcepb.QueryFieldType_DEFAULT:
+ q := bleve.NewMatchQuery(removeSmallTerms(req.Query)) // removeSmallTerms should be part of the analyzer
+ q.SetBoost(float64(field.Boost))
+ q.SetField(field.Name)
+ q.Analyzer = standard.Name // analyze the text
+ q.Operator = query.MatchQueryOperatorAnd // all terms must match
+ disjoin.AddQuery(q)
- // Query 1: Match the exact query string
- queryExact := bleve.NewMatchQuery(req.Query)
- queryExact.SetBoost(10.0)
- queryExact.SetField(resource.SEARCH_FIELD_TITLE)
- queryExact.Analyzer = keyword.Name // don't analyze the query input - treat it as a single token
- queryExact.Operator = query.MatchQueryOperatorAnd // This doesn't make a difference for keyword analyzer, we add it just to be explicit.
- searchQuery := bleve.NewDisjunctionQuery(queryExact)
+ case resourcepb.QueryFieldType_KEYWORD:
+ q := bleve.NewMatchQuery(req.Query)
+ q.SetBoost(float64(field.Boost))
+ q.SetField(field.Name)
+ q.Analyzer = keyword.Name // don't analyze the query input - treat it as a single token
+ disjoin.AddQuery(q)
- // Query 2: Phrase query with standard analyzer
- queryPhrase := bleve.NewMatchPhraseQuery(req.Query)
- queryPhrase.SetBoost(5.0)
- queryPhrase.SetField(resource.SEARCH_FIELD_TITLE)
- queryPhrase.Analyzer = standard.Name
- searchQuery.AddQuery(queryPhrase)
-
- // Query 3: Match query with standard analyzer
- queryAnalyzed := bleve.NewMatchQuery(removeSmallTerms(req.Query))
- queryAnalyzed.SetField(resource.SEARCH_FIELD_TITLE)
- queryAnalyzed.SetBoost(2.0)
- queryAnalyzed.Analyzer = standard.Name
- queryAnalyzed.Operator = query.MatchQueryOperatorAnd // Make sure all terms from the query are matched
- searchQuery.AddQuery(queryAnalyzed)
-
- queries = append(queries, searchQuery)
+ case resourcepb.QueryFieldType_PHRASE:
+ q := bleve.NewMatchPhraseQuery(req.Query)
+ q.SetBoost(float64(field.Boost))
+ q.SetField(field.Name)
+ q.Analyzer = standard.Name
+ disjoin.AddQuery(q)
+ }
+ }
+ }
}
switch len(queries) {
@@ -1872,7 +1898,7 @@ func (q *permissionScopedQuery) Searcher(ctx context.Context, i index.IndexReade
if err != nil {
return nil, err
}
- filteringSearcher := bleveSearch.NewFilteringSearcher(ctx, searcher, func(d *search.DocumentMatch) bool {
+ filteringSearcher := bleveSearch.NewFilteringSearcher(ctx, searcher, func(_ *search.SearchContext, d *search.DocumentMatch) bool {
// The doc ID has the format: ///
// IndexInternalID will be the same as the doc ID when using an in-memory index, but when using a file-based
// index it becomes a binary encoded number that has some other internal meaning. Using ExternalID() will get the
diff --git a/pkg/storage/unified/search/bleve_integration_test.go b/pkg/storage/unified/search/bleve_integration_test.go
index 819fd5a8d9a..1f34444574f 100644
--- a/pkg/storage/unified/search/bleve_integration_test.go
+++ b/pkg/storage/unified/search/bleve_integration_test.go
@@ -4,6 +4,7 @@ import (
"context"
"testing"
+ index "github.com/blevesearch/bleve_index_api"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/storage/unified/resource"
@@ -19,6 +20,7 @@ func TestBleveSearchBackend(t *testing.T) {
backend, err := NewBleveBackend(BleveOptions{
Root: tempDir,
FileThreshold: 5,
+ ScoringModel: index.BM25Scoring,
}, nil)
require.NoError(t, err)
require.NotNil(t, backend)
@@ -52,3 +54,32 @@ func TestSearchBackendBenchmark(t *testing.T) {
unitest.BenchmarkSearchBackend(t, backend, opts)
}
+
+func BenchmarkScoringModels(b *testing.B) {
+ models := []string{index.TFIDFScoring, index.BM25Scoring}
+
+ for _, model := range models {
+ b.Run(model, func(b *testing.B) {
+ tempDir := b.TempDir()
+
+ backend, err := NewBleveBackend(BleveOptions{
+ Root: tempDir,
+ ScoringModel: model,
+ }, nil)
+ require.NoError(b, err)
+ require.NotNil(b, backend)
+
+ b.Cleanup(backend.Stop)
+
+ opts := &unitest.BenchmarkOptions{
+ NumResources: 1000,
+ Concurrency: 4,
+ NumNamespaces: 10,
+ NumGroups: 10,
+ NumResourceTypes: 10,
+ }
+
+ unitest.BenchmarkSearchBackend(b, backend, opts)
+ })
+ }
+}
diff --git a/pkg/storage/unified/search/bleve_mappings.go b/pkg/storage/unified/search/bleve_mappings.go
index 43adcbc607e..20eb2ffb8df 100644
--- a/pkg/storage/unified/search/bleve_mappings.go
+++ b/pkg/storage/unified/search/bleve_mappings.go
@@ -5,13 +5,15 @@ import (
"github.com/blevesearch/bleve/v2/analysis/analyzer/keyword"
"github.com/blevesearch/bleve/v2/analysis/analyzer/standard"
"github.com/blevesearch/bleve/v2/mapping"
-
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
)
-func GetBleveMappings(fields resource.SearchableDocumentFields) (mapping.IndexMapping, error) {
+func GetBleveMappings(scoringModel string, fields resource.SearchableDocumentFields) (mapping.IndexMapping, error) {
mapper := bleve.NewIndexMapping()
+ if scoringModel != "" {
+ mapper.ScoringModel = scoringModel
+ }
err := RegisterCustomAnalyzers(mapper)
if err != nil {
diff --git a/pkg/storage/unified/search/bleve_mappings_test.go b/pkg/storage/unified/search/bleve_mappings_test.go
index 3b8027ee06e..821cf987990 100644
--- a/pkg/storage/unified/search/bleve_mappings_test.go
+++ b/pkg/storage/unified/search/bleve_mappings_test.go
@@ -13,7 +13,7 @@ import (
)
func TestDocumentMapping(t *testing.T) {
- mappings, err := search.GetBleveMappings(nil)
+ mappings, err := search.GetBleveMappings("", nil)
require.NoError(t, err)
data := resource.IndexableDocument{
Title: "title",
diff --git a/pkg/storage/unified/search/bleve_search_test.go b/pkg/storage/unified/search/bleve_search_test.go
index c10aa3f6726..b221a60a7d6 100644
--- a/pkg/storage/unified/search/bleve_search_test.go
+++ b/pkg/storage/unified/search/bleve_search_test.go
@@ -7,6 +7,7 @@ import (
"testing"
"github.com/blevesearch/bleve/v2"
+ index "github.com/blevesearch/bleve_index_api"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/apimachinery/identity"
@@ -258,6 +259,7 @@ func newTestDashboardsIndex(t testing.TB, threshold int64, size int64, writer re
backend, err := search.NewBleveBackend(search.BleveOptions{
Root: t.TempDir(),
FileThreshold: threshold, // use in-memory for tests
+ ScoringModel: index.BM25Scoring,
}, nil)
require.NoError(t, err)
diff --git a/pkg/storage/unified/search/bleve_test.go b/pkg/storage/unified/search/bleve_test.go
index c9c3967cd58..a88951a100a 100644
--- a/pkg/storage/unified/search/bleve_test.go
+++ b/pkg/storage/unified/search/bleve_test.go
@@ -14,6 +14,7 @@ import (
"time"
"github.com/blevesearch/bleve/v2"
+ index "github.com/blevesearch/bleve_index_api"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert"
@@ -50,6 +51,7 @@ func TestBleveBackend(t *testing.T) {
backend, err := NewBleveBackend(BleveOptions{
Root: tmpdir,
FileThreshold: 5, // with more than 5 items we create a file on disk
+ ScoringModel: index.BM25Scoring,
}, nil)
require.NoError(t, err)
t.Cleanup(backend.Stop)
@@ -773,6 +775,7 @@ func setupBleveBackend(t *testing.T, options ...setupOption) (*bleveBackend, pro
IndexCacheTTL: defaultIndexCacheTTL,
Logger: log.NewNopLogger(),
BuildVersion: buildVersion,
+ ScoringModel: index.BM25Scoring,
}
for _, opt := range options {
opt(&opts)
diff --git a/pkg/storage/unified/search/options.go b/pkg/storage/unified/search/options.go
index d450e9ae24b..64cf074f52c 100644
--- a/pkg/storage/unified/search/options.go
+++ b/pkg/storage/unified/search/options.go
@@ -46,6 +46,7 @@ func NewSearchOptions(
BuildVersion: cfg.BuildVersion,
OwnsIndex: ownsIndexFn,
IndexMinUpdateInterval: cfg.IndexMinUpdateInterval,
+ ScoringModel: cfg.IndexScoringModel,
}, indexMetrics)
if err != nil {
diff --git a/pkg/storage/unified/sql/db/migrations/resource_mig.go b/pkg/storage/unified/sql/db/migrations/resource_mig.go
index 8f9be689718..8f7abe1306e 100644
--- a/pkg/storage/unified/sql/db/migrations/resource_mig.go
+++ b/pkg/storage/unified/sql/db/migrations/resource_mig.go
@@ -2,8 +2,11 @@ package migrations
import (
"fmt"
+ "strings"
+ "github.com/bwmarrin/snowflake"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
+ "github.com/grafana/grafana/pkg/util/xorm"
)
func initResourceTables(mg *migrator.Migrator) string {
@@ -220,5 +223,142 @@ func initResourceTables(mg *migrator.Migrator) string {
mg.AddMigration("Change key_path collation of resource_history in postgres", migrator.NewRawSQLMigration("").Postgres(`ALTER TABLE resource_history ALTER COLUMN key_path TYPE VARCHAR(2048) COLLATE "C";`))
mg.AddMigration("Change key_path collation of resource_events in postgres", migrator.NewRawSQLMigration("").Postgres(`ALTER TABLE resource_events ALTER COLUMN key_path TYPE VARCHAR(2048) COLLATE "C";`))
+ mg.AddMigration("resource_history key_path backfill", &ResourceHistoryKeyPathBackfillMigration{})
+
return marker
}
+
+type ResourceHistoryKeyPathBackfillMigration struct {
+ migrator.MigrationBase
+}
+
+func (m *ResourceHistoryKeyPathBackfillMigration) SQL(_ migrator.Dialect) string {
+ return "resource_history key_path backfill code migration"
+}
+
+func (m *ResourceHistoryKeyPathBackfillMigration) Exec(sess *xorm.Session, mg *migrator.Migrator) error {
+ rows, err := getResourceHistoryRows(sess, mg, resourceHistoryRow{})
+ if err != nil {
+ return err
+ }
+
+ for len(rows) > 0 {
+ if err := updateResourceHistoryKeyPath(sess, rows); err != nil {
+ return err
+ }
+
+ rows, err = getResourceHistoryRows(sess, mg, rows[len(rows)-1])
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func updateResourceHistoryKeyPath(sess *xorm.Session, rows []resourceHistoryRow) error {
+ if len(rows) == 0 {
+ return nil
+ }
+
+ updates := []resourceHistoryRow{}
+
+ for _, row := range rows {
+ if row.KeyPath == "" {
+ row.KeyPath = parseKeyPath(row)
+ updates = append(updates, row)
+ }
+ }
+
+ if len(updates) == 0 {
+ return nil
+ }
+
+ guids := ""
+ setCases := "CASE"
+ for _, row := range updates {
+ guids += fmt.Sprintf("'%s',", row.GUID)
+ setCases += fmt.Sprintf(" WHEN guid = '%s' THEN '%s'", row.GUID, row.KeyPath)
+ }
+
+ guids = strings.TrimRight(guids, ",")
+ setCases += " ELSE key_path END "
+
+ // the query will look like this
+ // UPDATE resource_history
+ // SET key_path = CASE
+ // WHEN guid = '1402de51-669b-4206-8a6c-005a00eee6e3' then 'unified/data/folder.grafana.app/folders/default/cf6lylpvls000c/1998492888241012800~created~'
+ // WHEN guid = '8842cc56-f22b-45e1-82b1-99759cd443b3' then 'unified/data/dashboard.grafana.app/dashboards/default/adzvfhp/1998492902577144677~created~cf6lylpvls000c'
+ // ELSE key_path END
+ // WHERE guid IN ('1402de51-669b-4206-8a6c-005a00eee6e3', '8842cc56-f22b-45e1-82b1-99759cd443b3')
+ // AND key_path = '';
+ sql := fmt.Sprintf(`
+ UPDATE resource_history
+ SET key_path = %s
+ WHERE guid IN (%s)
+ AND key_path = '';
+ `, setCases, guids)
+
+ if _, err := sess.Exec(sql); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func parseKeyPath(row resourceHistoryRow) string {
+ var action string
+ switch row.Action {
+ case 1:
+ action = "created"
+ case 2:
+ action = "updated"
+ case 3:
+ action = "deleted"
+ }
+ return fmt.Sprintf("unified/data/%s/%s/%s/%s/%d~%s~%s", row.Group, row.Resource, row.Namespace, row.Name, snowflakeFromRv(row.ResourceVersion), action, row.Folder)
+}
+
+func snowflakeFromRv(rv int64) int64 {
+ return (((rv / 1000) - snowflake.Epoch) << (snowflake.NodeBits + snowflake.StepBits)) + (rv % 1000)
+}
+
+type resourceHistoryRow struct {
+ GUID string `xorm:"guid"`
+ Group string `xorm:"group"`
+ Resource string `xorm:"resource"`
+ Namespace string `xorm:"namespace"`
+ Name string `xorm:"name"`
+ ResourceVersion int64 `xorm:"resource_version"`
+ Action int64 `xorm:"action"`
+ Folder string `xorm:"folder"`
+ KeyPath string `xorm:"key_path"`
+}
+
+func getResourceHistoryRows(sess *xorm.Session, mg *migrator.Migrator, continueRow resourceHistoryRow) ([]resourceHistoryRow, error) {
+ var rows []resourceHistoryRow
+ cols := fmt.Sprintf(
+ "%s, %s, %s, %s, %s, %s, %s, %s, %s",
+ mg.Dialect.Quote("guid"),
+ mg.Dialect.Quote("group"),
+ mg.Dialect.Quote("resource"),
+ mg.Dialect.Quote("namespace"),
+ mg.Dialect.Quote("name"),
+ mg.Dialect.Quote("resource_version"),
+ mg.Dialect.Quote("action"),
+ mg.Dialect.Quote("folder"),
+ mg.Dialect.Quote("key_path"))
+ sql := fmt.Sprintf(`
+ SELECT %s
+ FROM resource_history
+ WHERE (resource_version > %d OR (resource_version = %d AND guid > '%s'))
+ AND key_path = ''
+ ORDER BY resource_version ASC, guid ASC
+ LIMIT 1000;
+ `, cols, continueRow.ResourceVersion, continueRow.ResourceVersion, continueRow.GUID)
+ if err := sess.SQL(sql).Find(&rows); err != nil {
+ return nil, err
+ }
+
+ return rows, nil
+}
diff --git a/pkg/storage/unified/sql/test/integration_test.go b/pkg/storage/unified/sql/test/integration_test.go
index 166e2dac372..f73bb61d679 100644
--- a/pkg/storage/unified/sql/test/integration_test.go
+++ b/pkg/storage/unified/sql/test/integration_test.go
@@ -6,6 +6,7 @@ import (
"testing"
"time"
+ index "github.com/blevesearch/bleve_index_api"
"github.com/go-jose/go-jose/v4/jwt"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
@@ -129,21 +130,28 @@ func TestIntegrationSearchAndStorage(t *testing.T) {
ctx := context.Background()
- // Create a new bleve backend
- search, err := search.NewBleveBackend(search.BleveOptions{
- FileThreshold: 0,
- Root: t.TempDir(),
- }, nil)
- require.NoError(t, err)
- require.NotNil(t, search)
- t.Cleanup(search.Stop)
+ scoringModels := []string{index.TFIDFScoring, index.BM25Scoring}
- // Create a new resource backend
- storage, _ := newTestBackend(t, false, 0)
- require.NotNil(t, storage)
+ for _, model := range scoringModels {
+ t.Run(model, func(t *testing.T) {
+ // Create a new bleve backend
+ search, err := search.NewBleveBackend(search.BleveOptions{
+ FileThreshold: 0,
+ Root: t.TempDir(),
+ ScoringModel: model,
+ }, nil)
+ require.NoError(t, err)
+ require.NotNil(t, search)
+ t.Cleanup(search.Stop)
- // Run the shared storage and search tests
- unitest.RunTestSearchAndStorage(t, ctx, storage, search)
+ // Create a new resource backend
+ storage, _ := newTestBackend(t, false, 0)
+ require.NotNil(t, storage)
+
+ // Run the shared storage and search tests
+ unitest.RunTestSearchAndStorage(t, ctx, storage, search)
+ })
+ }
}
func TestClientServer(t *testing.T) {
diff --git a/pkg/tests/api/elasticsearch/elasticsearch_test.go b/pkg/tests/api/elasticsearch/elasticsearch_test.go
index 09277c944f0..651dd74e9e2 100644
--- a/pkg/tests/api/elasticsearch/elasticsearch_test.go
+++ b/pkg/tests/api/elasticsearch/elasticsearch_test.go
@@ -24,6 +24,24 @@ func TestMain(m *testing.M) {
testsuite.Run(m)
}
+// mockElasticsearchHandler returns a handler that mocks Elasticsearch endpoints.
+// It responds to GET / with cluster info (required for datasource initialization)
+// and returns 401 Unauthorized for all other requests.
+func mockElasticsearchHandler(onRequest func(r *http.Request)) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ switch {
+ case r.Method == http.MethodGet && r.URL.Path == "/":
+ w.Header().Set("Content-Type", "application/json")
+ _, _ = w.Write([]byte(`{"version":{"build_flavor":"default","number":"8.0.0"}}`))
+ default:
+ if onRequest != nil {
+ onRequest(r)
+ }
+ w.WriteHeader(http.StatusUnauthorized)
+ }
+ }
+}
+
func TestIntegrationElasticsearch(t *testing.T) {
testutil.SkipIntegrationTestInShortMode(t)
@@ -35,9 +53,8 @@ func TestIntegrationElasticsearch(t *testing.T) {
ctx := context.Background()
var outgoingRequest *http.Request
- outgoingServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ outgoingServer := httptest.NewServer(mockElasticsearchHandler(func(r *http.Request) {
outgoingRequest = r
- w.WriteHeader(http.StatusUnauthorized)
}))
t.Cleanup(outgoingServer.Close)
diff --git a/pkg/tests/api/plugins/data/expectedListResp.json b/pkg/tests/api/plugins/data/expectedListResp.json
index 24f705eccd1..83debc4c410 100644
--- a/pkg/tests/api/plugins/data/expectedListResp.json
+++ b/pkg/tests/api/plugins/data/expectedListResp.json
@@ -209,7 +209,7 @@
"path": "public/plugins/grafana-azure-monitor-datasource/img/azure_monitor_cpu.png"
}
],
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": [
"azure",
@@ -589,7 +589,7 @@
"hasUpdate": false,
"defaultNavUrl": "/plugins/datagrid/",
"category": "",
- "state": "beta",
+ "state": "deprecated",
"signature": "internal",
"signatureType": "",
"signatureOrg": "",
@@ -639,7 +639,7 @@
]
},
"dependencies": {
- "grafanaDependency": "",
+ "grafanaDependency": "\u003e=11.6.0",
"grafanaVersion": "*",
"plugins": [],
"extensions": {
@@ -880,7 +880,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": null
},
@@ -934,7 +934,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": [
"grafana",
@@ -1000,7 +1000,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": null
},
@@ -1217,7 +1217,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": null
},
@@ -1325,7 +1325,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": null
},
@@ -1375,7 +1375,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": null
},
@@ -1425,7 +1425,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": null
},
@@ -1575,7 +1575,7 @@
},
"build": {},
"screenshots": null,
- "version": "",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": null
},
@@ -1629,7 +1629,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": [
"grafana",
@@ -1734,7 +1734,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": null
},
@@ -2042,7 +2042,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": null
},
@@ -2092,7 +2092,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": null
},
@@ -2445,7 +2445,7 @@
},
"build": {},
"screenshots": null,
- "version": "12.3.0-pre",
+ "version": "12.4.0-pre",
"updated": "",
"keywords": null
},
diff --git a/pkg/tests/apis/dashboard/search_test.go b/pkg/tests/apis/dashboard/search_test.go
index 2227c67287e..df03e6a9670 100644
--- a/pkg/tests/apis/dashboard/search_test.go
+++ b/pkg/tests/apis/dashboard/search_test.go
@@ -97,7 +97,7 @@ func TestIntegrationSearchDevDashboards(t *testing.T) {
require.Equal(t, 16, fileCount, "file count from %s", devenv)
// Helper to call search
- callSearch := func(user apis.User, params string) dashboardV0.SearchResults {
+ callSearch := func(user apis.User, params map[string]string) dashboardV0.SearchResults {
require.NotNil(t, user)
ns := user.Identity.GetNamespace()
cfg := dynamic.ConfigFor(user.NewRestConfig())
@@ -107,17 +107,12 @@ func TestIntegrationSearchDevDashboards(t *testing.T) {
var statusCode int
req := restClient.Get().AbsPath("apis", "dashboard.grafana.app", "v0alpha1", "namespaces", ns, "search").
+ //Param("explain", "true") // helpful to understand which field made things match
Param("limit", "1000").
Param("type", "dashboard") // Only search dashboards
- for kv := range strings.SplitSeq(params, "&") {
- if kv == "" {
- continue
- }
- parts := strings.SplitN(kv, "=", 2)
- if len(parts) == 2 {
- req = req.Param(parts[0], parts[1])
- }
+ for k, v := range params {
+ req = req.Param(k, v)
}
res := req.Do(ctx).StatusCode(&statusCode)
require.NoError(t, res.Error())
@@ -140,22 +135,47 @@ func TestIntegrationSearchDevDashboards(t *testing.T) {
testCases := []struct {
name string
user apis.User
- params string
+ params map[string]string
}{
{
- name: "all",
- user: helper.Org1.Admin,
- params: "", // only dashboards
+ name: "all",
+ user: helper.Org1.Admin,
},
{
- name: "simple-query",
- user: helper.Org1.Admin,
- params: "query=stacking",
+ name: "query-single-word",
+ user: helper.Org1.Admin,
+ params: map[string]string{
+ "query": "stacking",
+ },
},
{
- name: "with-text-panel",
- user: helper.Org1.Admin,
- params: "field=panel_types&panelType=text",
+ name: "query-multiple-words",
+ user: helper.Org1.Admin,
+ params: map[string]string{
+ "query": "graph softMin", // must match ALL terms
+ },
+ },
+ {
+ name: "with-text-panel",
+ user: helper.Org1.Admin,
+ params: map[string]string{
+ "field": "panel_types", // return panel types
+ "panelType": "text",
+ },
+ },
+ {
+ name: "title-ngram-prefix",
+ user: helper.Org1.Admin,
+ params: map[string]string{
+ "query": "zer", // should match "Zero Decimals Y Ticks"
+ },
+ },
+ {
+ name: "title-ngram-middle-word",
+ user: helper.Org1.Admin,
+ params: map[string]string{
+ "query": "decim", // should match "Zero Decimals Y Ticks"
+ },
},
}
for i, tc := range testCases {
diff --git a/pkg/tests/apis/dashboard/testdata/searchV0/t01-simple-query.json b/pkg/tests/apis/dashboard/testdata/searchV0/t01-query-single-word.json
similarity index 88%
rename from pkg/tests/apis/dashboard/testdata/searchV0/t01-simple-query.json
rename to pkg/tests/apis/dashboard/testdata/searchV0/t01-query-single-word.json
index 6c9a935dfe8..02eed11383a 100644
--- a/pkg/tests/apis/dashboard/testdata/searchV0/t01-simple-query.json
+++ b/pkg/tests/apis/dashboard/testdata/searchV0/t01-query-single-word.json
@@ -10,7 +10,7 @@
"panel-tests",
"graph-ng"
],
- "score": 0.658
+ "score": 0.284
},
{
"resource": "dashboards",
@@ -21,8 +21,8 @@
"panel-tests",
"graph-ng"
],
- "score": 0.625
+ "score": 0.269
}
],
- "maxScore": 0.658
+ "maxScore": 0.284
}
\ No newline at end of file
diff --git a/pkg/tests/apis/dashboard/testdata/searchV0/t02-query-multiple-words.json b/pkg/tests/apis/dashboard/testdata/searchV0/t02-query-multiple-words.json
new file mode 100644
index 00000000000..270801994c0
--- /dev/null
+++ b/pkg/tests/apis/dashboard/testdata/searchV0/t02-query-multiple-words.json
@@ -0,0 +1,17 @@
+{
+ "totalHits": 1,
+ "hits": [
+ {
+ "resource": "dashboards",
+ "name": "timeseries-soft-limits",
+ "title": "Panel Tests - Graph NG - softMin/softMax",
+ "tags": [
+ "gdev",
+ "panel-tests",
+ "graph-ng"
+ ],
+ "score": 0.024
+ }
+ ],
+ "maxScore": 0.024
+}
\ No newline at end of file
diff --git a/pkg/tests/apis/dashboard/testdata/searchV0/t02-with-text-panel.json b/pkg/tests/apis/dashboard/testdata/searchV0/t03-with-text-panel.json
similarity index 100%
rename from pkg/tests/apis/dashboard/testdata/searchV0/t02-with-text-panel.json
rename to pkg/tests/apis/dashboard/testdata/searchV0/t03-with-text-panel.json
diff --git a/pkg/tests/apis/dashboard/testdata/searchV0/t04-title-ngram-prefix.json b/pkg/tests/apis/dashboard/testdata/searchV0/t04-title-ngram-prefix.json
new file mode 100644
index 00000000000..8059db130a0
--- /dev/null
+++ b/pkg/tests/apis/dashboard/testdata/searchV0/t04-title-ngram-prefix.json
@@ -0,0 +1,17 @@
+{
+ "totalHits": 1,
+ "hits": [
+ {
+ "resource": "dashboards",
+ "name": "timeseries-y-ticks-zero-decimals",
+ "title": "Zero Decimals Y Ticks",
+ "tags": [
+ "gdev",
+ "panel-tests",
+ "graph-ng"
+ ],
+ "score": 0.35
+ }
+ ],
+ "maxScore": 0.35
+}
\ No newline at end of file
diff --git a/pkg/tests/apis/dashboard/testdata/searchV0/t05-title-ngram-middle-word.json b/pkg/tests/apis/dashboard/testdata/searchV0/t05-title-ngram-middle-word.json
new file mode 100644
index 00000000000..8059db130a0
--- /dev/null
+++ b/pkg/tests/apis/dashboard/testdata/searchV0/t05-title-ngram-middle-word.json
@@ -0,0 +1,17 @@
+{
+ "totalHits": 1,
+ "hits": [
+ {
+ "resource": "dashboards",
+ "name": "timeseries-y-ticks-zero-decimals",
+ "title": "Zero Decimals Y Ticks",
+ "tags": [
+ "gdev",
+ "panel-tests",
+ "graph-ng"
+ ],
+ "score": 0.35
+ }
+ ],
+ "maxScore": 0.35
+}
\ No newline at end of file
diff --git a/pkg/tests/apis/iam/team_bindings_integration_test.go b/pkg/tests/apis/iam/team_bindings_integration_test.go
index 1b355296486..40258edaf45 100644
--- a/pkg/tests/apis/iam/team_bindings_integration_test.go
+++ b/pkg/tests/apis/iam/team_bindings_integration_test.go
@@ -67,7 +67,7 @@ func TestIntegrationTeamBindings(t *testing.T) {
doTeamBindingCRUDTestsUsingTheNewAPIs(t, helper, team, user)
if mode < 3 {
- doTeamBindingCRUDTestsUsingTheLegacyAPIs(t, helper, mode)
+ doTeamBindingCRUDTestsUsingTheLegacyAPIs(t, helper)
}
})
}
@@ -84,13 +84,15 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
})
// Create the team binding
- toCreate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
- toCreate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
- toCreate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
+ toCreate := createTeamBindingObject(helper, user.GetName(), team.GetName())
created, err := teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
require.NoError(t, err)
require.NotNil(t, created)
+ defer func() {
+ _ = teamBindingClient.Resource.Delete(ctx, created.GetName(), metav1.DeleteOptions{})
+ }()
+
createdSpec := created.Object["spec"].(map[string]interface{})
require.Equal(t, user.GetName(), createdSpec["subject"].(map[string]interface{})["name"])
require.Equal(t, team.GetName(), createdSpec["teamRef"].(map[string]interface{})["name"])
@@ -115,6 +117,7 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
// Update the team binding
toUpdate := toCreate.DeepCopy()
toUpdate.Object["spec"].(map[string]interface{})["permission"] = "member"
+ toUpdate.Object["metadata"].(map[string]interface{})["name"] = createdUID
updated, err := teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
require.NoError(t, err)
require.NotNil(t, updated)
@@ -164,9 +167,7 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
GVR: gvrTeamBindings,
})
- toCreate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
- toCreate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
- toCreate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
+ toCreate := createTeamBindingObject(helper, user.GetName(), team.GetName())
_, err := teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
require.Error(t, err)
@@ -185,9 +186,7 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
GVR: gvrTeamBindings,
})
- toCreate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
- toCreate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = ""
- toCreate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
+ toCreate := createTeamBindingObject(helper, "", team.GetName())
_, err := teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
require.Error(t, err)
@@ -205,9 +204,7 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
GVR: gvrTeamBindings,
})
- toCreate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
- toCreate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
- toCreate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = ""
+ toCreate := createTeamBindingObject(helper, user.GetName(), "")
_, err := teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
require.Error(t, err)
@@ -225,9 +222,7 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
GVR: gvrTeamBindings,
})
- toCreate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
- toCreate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
- toCreate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
+ toCreate := createTeamBindingObject(helper, user.GetName(), team.GetName())
toCreate.Object["spec"].(map[string]interface{})["permission"] = "invalid"
_, err := teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
@@ -245,17 +240,31 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
} {
t.Run(fmt.Sprintf("with basic role_%s", u.Identity.GetOrgRole()), func(t *testing.T) {
ctx := context.Background()
+
+ // Create the team binding using admin
+ adminClient := helper.GetResourceClient(apis.ResourceClientArgs{
+ User: helper.Org1.Admin,
+ Namespace: helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID()),
+ GVR: gvrTeamBindings,
+ })
+
+ toCreate := createTeamBindingObject(helper, user.GetName(), team.GetName())
+ created, err := adminClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
+ require.NoError(t, err)
+
+ defer func() {
+ _ = adminClient.Resource.Delete(ctx, created.GetName(), metav1.DeleteOptions{})
+ }()
+
teamBindingClient := helper.GetResourceClient(apis.ResourceClientArgs{
User: u,
Namespace: helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID()),
GVR: gvrTeamBindings,
})
- toUpdate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
- toUpdate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
- toUpdate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
+ toUpdate := created.DeepCopy()
toUpdate.Object["spec"].(map[string]interface{})["permission"] = "member"
- _, err := teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
+ _, err = teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
require.Error(t, err)
var statusErr *errors.StatusError
@@ -273,10 +282,8 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
GVR: gvrTeamBindings,
})
- toUpdate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
+ toUpdate := createTeamBindingObject(helper, user.GetName(), team.GetName())
toUpdate.Object["metadata"].(map[string]interface{})["name"] = "invalid-team-binding-name"
- toUpdate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
- toUpdate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
_, err := teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
require.Error(t, err)
var statusErr *errors.StatusError
@@ -293,15 +300,18 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
GVR: gvrTeamBindings,
})
- // Create the team binding if it doesn't already exist
- toCreate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
- toCreate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
- toCreate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
- _, _ = teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
+ toCreate := createTeamBindingObject(helper, user.GetName(), team.GetName())
+ created, err := teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
+ require.NoError(t, err)
+
+ defer func() {
+ _ = teamBindingClient.Resource.Delete(ctx, created.GetName(), metav1.DeleteOptions{})
+ }()
toUpdate := toCreate.DeepCopy()
toUpdate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = "test-team-2"
- _, err := teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
+ toUpdate.Object["metadata"].(map[string]interface{})["name"] = created.GetName()
+ _, err = teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
require.Error(t, err)
var statusErr *errors.StatusError
require.ErrorAs(t, err, &statusErr)
@@ -317,16 +327,19 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
GVR: gvrTeamBindings,
})
- // Create the team binding if it doesn't already exist
- toCreate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
- toCreate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
- toCreate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
- _, _ = teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
+ toCreate := createTeamBindingObject(helper, user.GetName(), team.GetName())
+ created, err := teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
+ require.NoError(t, err)
+
+ defer func() {
+ _ = teamBindingClient.Resource.Delete(ctx, created.GetName(), metav1.DeleteOptions{})
+ }()
toUpdate := toCreate.DeepCopy()
+ toUpdate.Object["metadata"].(map[string]interface{})["name"] = created.GetName()
toUpdate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = "test-user-2"
- _, err := teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
+ _, err = teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
require.Error(t, err)
var statusErr *errors.StatusError
require.ErrorAs(t, err, &statusErr)
@@ -342,15 +355,18 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
GVR: gvrTeamBindings,
})
- // Create the team binding if it doesn't already exist
- toCreate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
- toCreate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
- toCreate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
- _, _ = teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
+ toCreate := createTeamBindingObject(helper, user.GetName(), team.GetName())
+ created, err := teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
+ require.NoError(t, err)
+
+ defer func() {
+ _ = teamBindingClient.Resource.Delete(ctx, created.GetName(), metav1.DeleteOptions{})
+ }()
toUpdate := toCreate.DeepCopy()
toUpdate.Object["spec"].(map[string]interface{})["external"] = true
- _, err := teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
+ toUpdate.Object["metadata"].(map[string]interface{})["name"] = created.GetName()
+ _, err = teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
require.Error(t, err)
var statusErr *errors.StatusError
require.ErrorAs(t, err, &statusErr)
@@ -366,17 +382,18 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
GVR: gvrTeamBindings,
})
- // Create the team binding if it doesn't already exist
- toCreate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
- toCreate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
- toCreate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
- _, _ = teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
+ toCreate := createTeamBindingObject(helper, user.GetName(), team.GetName())
+ created, err := teamBindingClient.Resource.Create(ctx, toCreate, metav1.CreateOptions{})
+ require.NoError(t, err)
- toUpdate := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
- toUpdate.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = user.GetName()
- toUpdate.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = team.GetName()
+ defer func() {
+ _ = teamBindingClient.Resource.Delete(ctx, created.GetName(), metav1.DeleteOptions{})
+ }()
+
+ toUpdate := createTeamBindingObject(helper, user.GetName(), team.GetName())
toUpdate.Object["spec"].(map[string]interface{})["permission"] = "invalid"
- _, err := teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
+ toUpdate.Object["metadata"].(map[string]interface{})["name"] = created.GetName()
+ _, err = teamBindingClient.Resource.Update(ctx, toUpdate, metav1.UpdateOptions{})
require.Error(t, err)
var statusErr *errors.StatusError
require.ErrorAs(t, err, &statusErr)
@@ -385,7 +402,7 @@ func doTeamBindingCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHel
})
}
-func doTeamBindingCRUDTestsUsingTheLegacyAPIs(t *testing.T, helper *apis.K8sTestHelper, mode rest.DualWriterMode) {
+func doTeamBindingCRUDTestsUsingTheLegacyAPIs(t *testing.T, helper *apis.K8sTestHelper) {
t.Run("should create team binding using legacy APIs and get it using the new APIs", func(t *testing.T) {
ctx := context.Background()
@@ -499,3 +516,10 @@ func doTeamBindingCRUDTestsUsingTheLegacyAPIs(t *testing.T, helper *apis.K8sTest
require.Equal(t, teamBindingName, teamBinding.GetName())
})
}
+
+func createTeamBindingObject(helper *apis.K8sTestHelper, userName, teamName string) *unstructured.Unstructured {
+ obj := helper.LoadYAMLOrJSONFile("testdata/teambinding-test-create-v0.yaml")
+ obj.Object["spec"].(map[string]interface{})["subject"].(map[string]interface{})["name"] = userName
+ obj.Object["spec"].(map[string]interface{})["teamRef"].(map[string]interface{})["name"] = teamName
+ return obj
+}
diff --git a/pkg/tests/apis/iam/testdata/teambinding-test-create-v0.yaml b/pkg/tests/apis/iam/testdata/teambinding-test-create-v0.yaml
index da04e7785b1..2ac36c1b6a6 100644
--- a/pkg/tests/apis/iam/testdata/teambinding-test-create-v0.yaml
+++ b/pkg/tests/apis/iam/testdata/teambinding-test-create-v0.yaml
@@ -1,7 +1,7 @@
apiVersion: iam.grafana.app/v0alpha1
kind: TeamBinding
metadata:
- name: test-team-binding-1
+ generateName: test-team-binding-
spec:
subject:
name: ""
diff --git a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json
index b89d431883b..244a8e591b1 100644
--- a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json
+++ b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json
@@ -1788,11 +1788,11 @@
"default": false
},
"valuesFormat": {
+ "type": "string",
"enum": [
"csv",
"json"
- ],
- "type": "string"
+ ]
}
},
"additionalProperties": false
@@ -2242,6 +2242,10 @@
"description": "This can be used by data sources that return and explicit naming structure for values and labels\nWhen this property is configured, this value is used rather than the default naming strategy.",
"type": "string"
},
+ "fieldMinMax": {
+ "description": "Calculate min max per field",
+ "type": "boolean"
+ },
"filterable": {
"description": "True if data source field supports ad-hoc filters",
"type": "boolean"
@@ -2273,6 +2277,9 @@
"description": "Alternative to empty string",
"type": "string"
},
+ "nullValueMode": {
+ "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardNullValueMode"
+ },
"path": {
"description": "An explicit path to the field in the datasource. When the frame meta includes a path,\nThis will default to `${frame.meta.path}/${field.name}\n\nWhen defined, this value can be used as an identifier within the datasource scope, and\nmay be used to update the results",
"type": "string"
@@ -2281,7 +2288,7 @@
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardThresholdsConfig"
},
"unit": {
- "description": "Unit a field should use. The unit you select is applied to all fields except time.\nYou can use the units ID availables in Grafana or a custom unit.\nAvailable units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts\nAs custom unit, you can use the following formats:\n`suffix:` for custom unit that should go after value.\n`prefix:` for custom unit that should go before value.\n`time:` For custom date time formats type for example `time:YYYY-MM-DD`.\n`si:` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character.\n`count:` for a custom count unit.\n`currency:` for custom a currency unit.",
+ "description": "Unit a field should use. The unit you select is applied to all fields except time.\nYou can use the units ID availables in Grafana or a custom unit.\nAvailable units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts\nAs custom unit, you can use the following formats:\n`suffix:\u003csuffix\u003e` for custom unit that should go after value.\n`prefix:\u003cprefix\u003e` for custom unit that should go before value.\n`time:\u003cformat\u003e` For custom date time formats type for example `time:YYYY-MM-DD`.\n`si:\u003cbase scale\u003e\u003cunit characters\u003e` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character.\n`count:\u003cunit\u003e` for a custom count unit.\n`currency:\u003cunit\u003e` for custom a currency unit.",
"type": "string"
},
"writeable": {
@@ -2774,6 +2781,15 @@
},
"additionalProperties": false
},
+ "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardNullValueMode": {
+ "description": "How null values should be handled",
+ "type": "string",
+ "enum": [
+ "null",
+ "connected",
+ "null as zero"
+ ]
+ },
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelKind": {
"type": "object",
"required": [
@@ -3797,7 +3813,7 @@
],
"properties": {
"options": {
- "description": "Map with : ValueMappingResult. For example: { \"10\": { text: \"Perfection!\", color: \"green\" } }",
+ "description": "Map with \u003cvalue_to_match\u003e: ValueMappingResult. For example: { \"10\": { text: \"Perfection!\", color: \"green\" } }",
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMappingResult"
@@ -4221,7 +4237,7 @@
}
},
"io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1": {
- "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
+ "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:\u003cname\u003e', where \u003cname\u003e is the name of a field in a struct, or key in a map 'v:\u003cvalue\u003e', where \u003cvalue\u003e is the exact json formatted value of a list item 'i:\u003cindex\u003e', where \u003cindex\u003e is position of a item in a list 'k:\u003ckeys\u003e', where \u003ckeys\u003e is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
"type": "object"
},
"io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": {
@@ -4575,4 +4591,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2beta1.json b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2beta1.json
index 7f396bc20d4..8588ee9707a 100644
--- a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2beta1.json
+++ b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2beta1.json
@@ -2261,6 +2261,10 @@
"description": "This can be used by data sources that return and explicit naming structure for values and labels\nWhen this property is configured, this value is used rather than the default naming strategy.",
"type": "string"
},
+ "fieldMinMax": {
+ "description": "Calculate min max per field",
+ "type": "boolean"
+ },
"filterable": {
"description": "True if data source field supports ad-hoc filters",
"type": "boolean"
@@ -2292,6 +2296,9 @@
"description": "Alternative to empty string",
"type": "string"
},
+ "nullValueMode": {
+ "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardNullValueMode"
+ },
"path": {
"description": "An explicit path to the field in the datasource. When the frame meta includes a path,\nThis will default to `${frame.meta.path}/${field.name}\n\nWhen defined, this value can be used as an identifier within the datasource scope, and\nmay be used to update the results",
"type": "string"
@@ -2300,7 +2307,7 @@
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardThresholdsConfig"
},
"unit": {
- "description": "Unit a field should use. The unit you select is applied to all fields except time.\nYou can use the units ID availables in Grafana or a custom unit.\nAvailable units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts\nAs custom unit, you can use the following formats:\n`suffix:` for custom unit that should go after value.\n`prefix:` for custom unit that should go before value.\n`time:` For custom date time formats type for example `time:YYYY-MM-DD`.\n`si:` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character.\n`count:` for a custom count unit.\n`currency:` for custom a currency unit.",
+ "description": "Unit a field should use. The unit you select is applied to all fields except time.\nYou can use the units ID availables in Grafana or a custom unit.\nAvailable units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts\nAs custom unit, you can use the following formats:\n`suffix:\u003csuffix\u003e` for custom unit that should go after value.\n`prefix:\u003cprefix\u003e` for custom unit that should go before value.\n`time:\u003cformat\u003e` For custom date time formats type for example `time:YYYY-MM-DD`.\n`si:\u003cbase scale\u003e\u003cunit characters\u003e` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character.\n`count:\u003cunit\u003e` for a custom count unit.\n`currency:\u003cunit\u003e` for custom a currency unit.",
"type": "string"
},
"writeable": {
@@ -2803,6 +2810,15 @@
},
"additionalProperties": false
},
+ "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardNullValueMode": {
+ "description": "How null values should be handled",
+ "type": "string",
+ "enum": [
+ "null",
+ "connected",
+ "null as zero"
+ ]
+ },
"com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardPanelKind": {
"type": "object",
"required": [
@@ -3823,7 +3839,7 @@
],
"properties": {
"options": {
- "description": "Map with : ValueMappingResult. For example: { \"10\": { text: \"Perfection!\", color: \"green\" } }",
+ "description": "Map with \u003cvalue_to_match\u003e: ValueMappingResult. For example: { \"10\": { text: \"Perfection!\", color: \"green\" } }",
"type": "object",
"additionalProperties": {
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardValueMappingResult"
@@ -4252,7 +4268,7 @@
}
},
"io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1": {
- "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:', where is the name of a field in a struct, or key in a map 'v:', where is the exact json formatted value of a list item 'i:', where is position of a item in a list 'k:', where is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
+ "description": "FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format.\n\nEach key is either a '.' representing the field itself, and will always map to an empty set, or a string representing a sub-field or item. The string will follow one of these four formats: 'f:\u003cname\u003e', where \u003cname\u003e is the name of a field in a struct, or key in a map 'v:\u003cvalue\u003e', where \u003cvalue\u003e is the exact json formatted value of a list item 'i:\u003cindex\u003e', where \u003cindex\u003e is position of a item in a list 'k:\u003ckeys\u003e', where \u003ckeys\u003e is a map of a list item's key fields to their unique values If a key maps to an empty Fields value, the field that key represents is part of the set.\n\nThe exact format is defined in sigs.k8s.io/structured-merge-diff",
"type": "object"
},
"io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": {
@@ -4606,4 +4622,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/pkg/tests/apis/provisioning/connection_test.go b/pkg/tests/apis/provisioning/connection_test.go
index 98418f7b54e..95d03e0a03e 100644
--- a/pkg/tests/apis/provisioning/connection_test.go
+++ b/pkg/tests/apis/provisioning/connection_test.go
@@ -166,8 +166,24 @@ func TestIntegrationProvisioning_ConnectionCRUDL(t *testing.T) {
githubInfo = spec["github"].(map[string]any)
assert.Equal(t, "454546", githubInfo["installationID"], "installationID should be updated")
- // DELETE
- require.NoError(t, helper.Connections.Resource.Delete(ctx, "connection", metav1.DeleteOptions{}), "failed to delete resource")
+ // DELETE - Retry delete to handle resource version conflicts
+ // The controller may have updated the resource after our update, changing the resource version
+ require.Eventually(t, func() bool {
+ err := helper.Connections.Resource.Delete(ctx, "connection", metav1.DeleteOptions{})
+ if err != nil {
+ if k8serrors.IsConflict(err) {
+ // Resource version conflict - retry
+ return false
+ }
+ if k8serrors.IsNotFound(err) {
+ // Already deleted - success
+ return true
+ }
+ // Other error - fail the test
+ require.NoError(t, err, "failed to delete resource")
+ }
+ return true
+ }, 5*time.Second, 100*time.Millisecond, "should successfully delete resource")
list, err = helper.Connections.Resource.List(ctx, metav1.ListOptions{})
require.NoError(t, err, "failed to list resources")
assert.Equal(t, 0, len(list.Items), "should have no connections")
diff --git a/pkg/tsdb/cloud-monitoring/cloudmonitoring.go b/pkg/tsdb/cloud-monitoring/cloudmonitoring.go
index c88feb30a1a..37401de02c4 100644
--- a/pkg/tsdb/cloud-monitoring/cloudmonitoring.go
+++ b/pkg/tsdb/cloud-monitoring/cloudmonitoring.go
@@ -92,7 +92,7 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
}, nil
}
- url := fmt.Sprintf("%v/v3/projects/%v/metricDescriptors", dsInfo.services[cloudMonitor].url, defaultProject)
+ url := fmt.Sprintf("%s/v3/projects/%s/metricDescriptors", dsInfo.services[cloudMonitor].url, defaultProject)
request, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
@@ -139,6 +139,7 @@ type datasourceInfo struct {
defaultProject string
clientEmail string
tokenUri string
+ universeDomain string
services map[string]datasourceService
privateKey string
usingImpersonation bool
@@ -150,6 +151,7 @@ type datasourceJSONData struct {
DefaultProject string `json:"defaultProject"`
ClientEmail string `json:"clientEmail"`
TokenURI string `json:"tokenUri"`
+ UniverseDomain string `json:"universeDomain"`
UsingImpersonation bool `json:"usingImpersonation"`
ServiceAccountToImpersonate string `json:"serviceAccountToImpersonate"`
}
@@ -179,6 +181,7 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst
defaultProject: jsonData.DefaultProject,
clientEmail: jsonData.ClientEmail,
tokenUri: jsonData.TokenURI,
+ universeDomain: jsonData.UniverseDomain,
usingImpersonation: jsonData.UsingImpersonation,
serviceAccountToImpersonate: jsonData.ServiceAccountToImpersonate,
services: map[string]datasourceService{},
@@ -194,13 +197,13 @@ func newInstanceSettings(httpClientProvider httpclient.Provider) datasource.Inst
return nil, err
}
- for name, info := range routes {
+ for name := range routes {
client, err := newHTTPClient(dsInfo, opts, &httpClientProvider, name)
if err != nil {
return nil, err
}
dsInfo.services[name] = datasourceService{
- url: info.url,
+ url: buildURL(name, dsInfo.universeDomain),
client: client,
}
}
diff --git a/pkg/tsdb/cloud-monitoring/httpclient.go b/pkg/tsdb/cloud-monitoring/httpclient.go
index 5a57e5f4ac8..aa0ed084194 100644
--- a/pkg/tsdb/cloud-monitoring/httpclient.go
+++ b/pkg/tsdb/cloud-monitoring/httpclient.go
@@ -23,12 +23,12 @@ type routeInfo struct {
var routes = map[string]routeInfo{
cloudMonitor: {
method: "GET",
- url: "https://monitoring.googleapis.com",
+ url: "https://monitoring.",
scopes: []string{cloudMonitorScope},
},
resourceManager: {
method: "GET",
- url: "https://cloudresourcemanager.googleapis.com",
+ url: "https://cloudresourcemanager.",
scopes: []string{resourceManagerScope},
},
}
@@ -68,6 +68,13 @@ func getMiddleware(model *datasourceInfo, routePath string) (httpclient.Middlewa
return tokenprovider.AuthMiddleware(provider), nil
}
+func buildURL(route string, universeDomain string) string {
+ if universeDomain == "" {
+ universeDomain = "googleapis.com"
+ }
+ return routes[route].url + universeDomain
+}
+
func newHTTPClient(model *datasourceInfo, opts httpclient.Options, clientProvider *httpclient.Provider, route string) (*http.Client, error) {
m, err := getMiddleware(model, route)
if err != nil {
diff --git a/pkg/tsdb/cloud-monitoring/resource_handler_test.go b/pkg/tsdb/cloud-monitoring/resource_handler_test.go
index 5f315ef9a59..64742233453 100644
--- a/pkg/tsdb/cloud-monitoring/resource_handler_test.go
+++ b/pkg/tsdb/cloud-monitoring/resource_handler_test.go
@@ -111,7 +111,7 @@ func Test_setRequestVariables(t *testing.T) {
im: &fakeInstance{
services: map[string]datasourceService{
cloudMonitor: {
- url: routes[cloudMonitor].url,
+ url: buildURL(cloudMonitor, "googleapis.com"),
client: &http.Client{},
},
},
diff --git a/pkg/tsdb/elasticsearch/aggregation_factory.go b/pkg/tsdb/elasticsearch/aggregation_factory.go
index cc3e597e50b..3f702b745b4 100644
--- a/pkg/tsdb/elasticsearch/aggregation_factory.go
+++ b/pkg/tsdb/elasticsearch/aggregation_factory.go
@@ -3,8 +3,8 @@ package elasticsearch
import (
"regexp"
- "github.com/grafana/grafana/pkg/components/simplejson"
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
+ "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
// addDateHistogramAgg adds a date histogram aggregation to the aggregation builder
diff --git a/pkg/tsdb/elasticsearch/client/client.go b/pkg/tsdb/elasticsearch/client/client.go
index fbb3e09f092..12e1a8f5df4 100644
--- a/pkg/tsdb/elasticsearch/client/client.go
+++ b/pkg/tsdb/elasticsearch/client/client.go
@@ -16,7 +16,6 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/backend/tracing"
- "github.com/grafana/grafana/pkg/services/featuremgmt"
)
// Used in logging to mark a stage
@@ -35,6 +34,7 @@ type DatasourceInfo struct {
Interval string
MaxConcurrentShardRequests int64
IncludeFrozen bool
+ ClusterInfo ClusterInfo
}
type ConfiguredFields struct {
@@ -159,7 +159,7 @@ func (c *baseClientImpl) ExecuteMultisearch(r *MultiSearchRequest) (*MultiSearch
resSpan.End()
}()
- improvedParsingEnabled := isFeatureEnabled(c.ctx, featuremgmt.FlagElasticsearchImprovedParsing)
+ improvedParsingEnabled := isFeatureEnabled(c.ctx, "elasticsearchImprovedParsing")
msr, err := c.parser.parseMultiSearchResponse(res.Body, improvedParsingEnabled)
if err != nil {
return nil, err
@@ -197,7 +197,11 @@ func (c *baseClientImpl) createMultiSearchRequests(searchRequests []*SearchReque
func (c *baseClientImpl) getMultiSearchQueryParameters() string {
var qs []string
- qs = append(qs, fmt.Sprintf("max_concurrent_shard_requests=%d", c.ds.MaxConcurrentShardRequests))
+ // if the build flavor is not serverless, we can use the max concurrent shard requests
+ // this is because serverless clusters do not support max concurrent shard requests
+ if !c.ds.ClusterInfo.IsServerless() && c.ds.MaxConcurrentShardRequests > 0 {
+ qs = append(qs, fmt.Sprintf("max_concurrent_shard_requests=%d", c.ds.MaxConcurrentShardRequests))
+ }
if c.ds.IncludeFrozen {
qs = append(qs, "ignore_throttled=false")
diff --git a/pkg/tsdb/elasticsearch/client/client_test.go b/pkg/tsdb/elasticsearch/client/client_test.go
index 8f257873232..b8afb048c00 100644
--- a/pkg/tsdb/elasticsearch/client/client_test.go
+++ b/pkg/tsdb/elasticsearch/client/client_test.go
@@ -15,7 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "github.com/grafana/grafana/pkg/components/simplejson"
+ "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
func TestClient_ExecuteMultisearch(t *testing.T) {
diff --git a/pkg/tsdb/elasticsearch/client/cluster_info.go b/pkg/tsdb/elasticsearch/client/cluster_info.go
new file mode 100644
index 00000000000..eb89189804f
--- /dev/null
+++ b/pkg/tsdb/elasticsearch/client/cluster_info.go
@@ -0,0 +1,51 @@
+package es
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+)
+
+type VersionInfo struct {
+ BuildFlavor string `json:"build_flavor"`
+}
+
+// ClusterInfo represents Elasticsearch cluster information returned from the root endpoint.
+// It is used to determine cluster capabilities and configuration like whether the cluster is serverless.
+type ClusterInfo struct {
+ Version VersionInfo `json:"version"`
+}
+
+const (
+ BuildFlavorServerless = "serverless"
+)
+
+// GetClusterInfo fetches cluster information from the Elasticsearch root endpoint.
+// It returns the cluster build flavor which is used to determine if the cluster is serverless.
+func GetClusterInfo(httpCli *http.Client, url string) (clusterInfo ClusterInfo, err error) {
+ resp, err := httpCli.Get(url)
+ if err != nil {
+ return ClusterInfo{}, fmt.Errorf("error getting ES cluster info: %w", err)
+ }
+
+ if resp.StatusCode != http.StatusOK {
+ return ClusterInfo{}, fmt.Errorf("unexpected status code %d getting ES cluster info", resp.StatusCode)
+ }
+
+ defer func() {
+ if closeErr := resp.Body.Close(); closeErr != nil && err == nil {
+ err = fmt.Errorf("error closing response body: %w", closeErr)
+ }
+ }()
+
+ err = json.NewDecoder(resp.Body).Decode(&clusterInfo)
+ if err != nil {
+ return ClusterInfo{}, fmt.Errorf("error decoding ES cluster info: %w", err)
+ }
+
+ return clusterInfo, nil
+}
+
+func (ci ClusterInfo) IsServerless() bool {
+ return ci.Version.BuildFlavor == BuildFlavorServerless
+}
diff --git a/pkg/tsdb/elasticsearch/client/cluster_info_test.go b/pkg/tsdb/elasticsearch/client/cluster_info_test.go
new file mode 100644
index 00000000000..0fdcc46e813
--- /dev/null
+++ b/pkg/tsdb/elasticsearch/client/cluster_info_test.go
@@ -0,0 +1,188 @@
+package es
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGetClusterInfo(t *testing.T) {
+ t.Run("Should successfully get cluster info", func(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ rw.Header().Set("Content-Type", "application/json")
+ _, err := rw.Write([]byte(`{
+ "name": "test-cluster",
+ "cluster_name": "elasticsearch",
+ "cluster_uuid": "abc123",
+ "version": {
+ "number": "8.0.0",
+ "build_flavor": "default",
+ "build_type": "tar",
+ "build_hash": "abc123",
+ "build_date": "2023-01-01T00:00:00.000Z",
+ "build_snapshot": false,
+ "lucene_version": "9.0.0"
+ }
+ }`))
+ require.NoError(t, err)
+ }))
+
+ t.Cleanup(func() {
+ ts.Close()
+ })
+
+ clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
+
+ require.NoError(t, err)
+ require.NotNil(t, clusterInfo)
+ assert.Equal(t, "default", clusterInfo.Version.BuildFlavor)
+ })
+
+ t.Run("Should successfully get serverless cluster info", func(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ rw.Header().Set("Content-Type", "application/json")
+ _, err := rw.Write([]byte(`{
+ "name": "serverless-cluster",
+ "cluster_name": "elasticsearch",
+ "cluster_uuid": "def456",
+ "version": {
+ "number": "8.11.0",
+ "build_flavor": "serverless",
+ "build_type": "docker",
+ "build_hash": "def456",
+ "build_date": "2023-11-01T00:00:00.000Z",
+ "build_snapshot": false,
+ "lucene_version": "9.8.0"
+ }
+ }`))
+ require.NoError(t, err)
+ }))
+
+ t.Cleanup(func() {
+ ts.Close()
+ })
+
+ clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
+
+ require.NoError(t, err)
+ require.NotNil(t, clusterInfo)
+ assert.Equal(t, "serverless", clusterInfo.Version.BuildFlavor)
+ assert.True(t, clusterInfo.IsServerless())
+ })
+
+ t.Run("Should return error when HTTP request fails", func(t *testing.T) {
+ clusterInfo, err := GetClusterInfo(http.DefaultClient, "http://invalid-url-that-does-not-exist.local:9999")
+
+ require.Error(t, err)
+ require.Equal(t, ClusterInfo{}, clusterInfo)
+ assert.Contains(t, err.Error(), "error getting ES cluster info")
+ })
+
+ t.Run("Should return error when response body is invalid JSON", func(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ rw.Header().Set("Content-Type", "application/json")
+ _, err := rw.Write([]byte(`{"invalid json`))
+ require.NoError(t, err)
+ }))
+
+ t.Cleanup(func() {
+ ts.Close()
+ })
+
+ clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
+
+ require.Error(t, err)
+ require.Equal(t, ClusterInfo{}, clusterInfo)
+ assert.Contains(t, err.Error(), "error decoding ES cluster info")
+ })
+
+ t.Run("Should handle empty version object", func(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ rw.Header().Set("Content-Type", "application/json")
+ _, err := rw.Write([]byte(`{
+ "name": "test-cluster",
+ "version": {}
+ }`))
+ require.NoError(t, err)
+ }))
+
+ t.Cleanup(func() {
+ ts.Close()
+ })
+
+ clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
+
+ require.NoError(t, err)
+ require.Equal(t, ClusterInfo{}, clusterInfo)
+ assert.Equal(t, "", clusterInfo.Version.BuildFlavor)
+ assert.False(t, clusterInfo.IsServerless())
+ })
+
+ t.Run("Should handle HTTP error status codes", func(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ rw.WriteHeader(http.StatusUnauthorized)
+ _, err := rw.Write([]byte(`{"error": "Unauthorized"}`))
+ require.NoError(t, err)
+ }))
+
+ t.Cleanup(func() {
+ ts.Close()
+ })
+
+ clusterInfo, err := GetClusterInfo(ts.Client(), ts.URL)
+
+ require.Error(t, err)
+ require.Equal(t, ClusterInfo{}, clusterInfo)
+ assert.Contains(t, err.Error(), "unexpected status code 401 getting ES cluster info")
+ })
+}
+
+func TestClusterInfo_IsServerless(t *testing.T) {
+ t.Run("Should return true when build_flavor is serverless", func(t *testing.T) {
+ clusterInfo := ClusterInfo{
+ Version: VersionInfo{
+ BuildFlavor: BuildFlavorServerless,
+ },
+ }
+
+ assert.True(t, clusterInfo.IsServerless())
+ })
+
+ t.Run("Should return false when build_flavor is default", func(t *testing.T) {
+ clusterInfo := ClusterInfo{
+ Version: VersionInfo{
+ BuildFlavor: "default",
+ },
+ }
+
+ assert.False(t, clusterInfo.IsServerless())
+ })
+
+ t.Run("Should return false when build_flavor is empty", func(t *testing.T) {
+ clusterInfo := ClusterInfo{
+ Version: VersionInfo{
+ BuildFlavor: "",
+ },
+ }
+
+ assert.False(t, clusterInfo.IsServerless())
+ })
+
+ t.Run("Should return false when build_flavor is unknown value", func(t *testing.T) {
+ clusterInfo := ClusterInfo{
+ Version: VersionInfo{
+ BuildFlavor: "unknown",
+ },
+ }
+
+ assert.False(t, clusterInfo.IsServerless())
+ })
+
+ t.Run("should return false when cluster info is empty", func(t *testing.T) {
+ clusterInfo := ClusterInfo{}
+ assert.False(t, clusterInfo.IsServerless())
+ })
+}
diff --git a/pkg/tsdb/elasticsearch/client/search_request_test.go b/pkg/tsdb/elasticsearch/client/search_request_test.go
index 80113b4996e..7e2c592dddb 100644
--- a/pkg/tsdb/elasticsearch/client/search_request_test.go
+++ b/pkg/tsdb/elasticsearch/client/search_request_test.go
@@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/grafana/grafana-plugin-sdk-go/backend"
- "github.com/grafana/grafana/pkg/components/simplejson"
+ "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
func TestSearchRequest(t *testing.T) {
diff --git a/pkg/tsdb/elasticsearch/data_query_processor.go b/pkg/tsdb/elasticsearch/data_query_processor.go
index 288d6ce30de..4dc0afb109a 100644
--- a/pkg/tsdb/elasticsearch/data_query_processor.go
+++ b/pkg/tsdb/elasticsearch/data_query_processor.go
@@ -6,8 +6,8 @@ import (
"strconv"
"github.com/grafana/grafana-plugin-sdk-go/backend"
- "github.com/grafana/grafana/pkg/components/simplejson"
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
+ "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
// processQuery processes a single query and adds it to the multi-search request builder
diff --git a/pkg/tsdb/elasticsearch/data_query_settings.go b/pkg/tsdb/elasticsearch/data_query_settings.go
index fe286ccaeda..519eb6dc96d 100644
--- a/pkg/tsdb/elasticsearch/data_query_settings.go
+++ b/pkg/tsdb/elasticsearch/data_query_settings.go
@@ -3,7 +3,7 @@ package elasticsearch
import (
"strconv"
- "github.com/grafana/grafana/pkg/components/simplejson"
+ "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
// setFloatPath converts a string value at the specified path to float64
diff --git a/pkg/tsdb/elasticsearch/elasticsearch.go b/pkg/tsdb/elasticsearch/elasticsearch.go
index 40073bf1740..0432bbcee20 100644
--- a/pkg/tsdb/elasticsearch/elasticsearch.go
+++ b/pkg/tsdb/elasticsearch/elasticsearch.go
@@ -88,6 +88,14 @@ func newInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins
httpCliOpts.SigV4.Service = "es"
}
+ apiKeyAuth, ok := jsonData["apiKeyAuth"].(bool)
+ if ok && apiKeyAuth {
+ apiKey := settings.DecryptedSecureJSONData["apiKey"]
+ if apiKey != "" {
+ httpCliOpts.Header.Add("Authorization", "ApiKey "+apiKey)
+ }
+ }
+
httpCli, err := httpClientProvider.New(httpCliOpts)
if err != nil {
return nil, err
@@ -151,6 +159,11 @@ func newInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins
includeFrozen = false
}
+ clusterInfo, err := es.GetClusterInfo(httpCli, settings.URL)
+ if err != nil {
+ return nil, err
+ }
+
configuredFields := es.ConfiguredFields{
TimeField: timeField,
LogLevelField: logLevelField,
@@ -166,6 +179,7 @@ func newInstanceSettings(httpClientProvider *httpclient.Provider) datasource.Ins
ConfiguredFields: configuredFields,
Interval: interval,
IncludeFrozen: includeFrozen,
+ ClusterInfo: clusterInfo,
}
return model, nil
}
diff --git a/pkg/tsdb/elasticsearch/elasticsearch_test.go b/pkg/tsdb/elasticsearch/elasticsearch_test.go
index 8ab3cabc7e5..35ec1f814ce 100644
--- a/pkg/tsdb/elasticsearch/elasticsearch_test.go
+++ b/pkg/tsdb/elasticsearch/elasticsearch_test.go
@@ -3,6 +3,8 @@ package elasticsearch
import (
"context"
"encoding/json"
+ "net/http"
+ "net/http/httptest"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
@@ -18,8 +20,26 @@ type datasourceInfo struct {
Interval string `json:"interval"`
}
+// mockElasticsearchServer creates a test HTTP server that mocks Elasticsearch cluster info endpoint
+func mockElasticsearchServer() *httptest.Server {
+ return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ // Return a mock Elasticsearch cluster info response
+ _ = json.NewEncoder(w).Encode(map[string]interface{}{
+ "version": map[string]interface{}{
+ "build_flavor": "serverless",
+ "number": "8.0.0",
+ },
+ })
+ }))
+}
+
func TestNewInstanceSettings(t *testing.T) {
t.Run("fields exist", func(t *testing.T) {
+ server := mockElasticsearchServer()
+ defer server.Close()
+
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: 5,
@@ -28,6 +48,7 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
+ URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -37,6 +58,9 @@ func TestNewInstanceSettings(t *testing.T) {
t.Run("timeField", func(t *testing.T) {
t.Run("is nil", func(t *testing.T) {
+ server := mockElasticsearchServer()
+ defer server.Close()
+
dsInfo := datasourceInfo{
MaxConcurrentShardRequests: 5,
Interval: "Daily",
@@ -46,6 +70,7 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
+ URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -54,6 +79,9 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("is empty", func(t *testing.T) {
+ server := mockElasticsearchServer()
+ defer server.Close()
+
dsInfo := datasourceInfo{
MaxConcurrentShardRequests: 5,
Interval: "Daily",
@@ -64,6 +92,7 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
+ URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -74,6 +103,9 @@ func TestNewInstanceSettings(t *testing.T) {
t.Run("maxConcurrentShardRequests", func(t *testing.T) {
t.Run("no maxConcurrentShardRequests", func(t *testing.T) {
+ server := mockElasticsearchServer()
+ defer server.Close()
+
dsInfo := datasourceInfo{
TimeField: "@timestamp",
}
@@ -81,6 +113,7 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
+ URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -90,6 +123,9 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("string maxConcurrentShardRequests", func(t *testing.T) {
+ server := mockElasticsearchServer()
+ defer server.Close()
+
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: "10",
@@ -98,6 +134,7 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
+ URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -107,6 +144,9 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("number maxConcurrentShardRequests", func(t *testing.T) {
+ server := mockElasticsearchServer()
+ defer server.Close()
+
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: 10,
@@ -115,6 +155,7 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
+ URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -124,6 +165,9 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("zero maxConcurrentShardRequests", func(t *testing.T) {
+ server := mockElasticsearchServer()
+ defer server.Close()
+
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: 0,
@@ -132,6 +176,7 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
+ URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -141,6 +186,9 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("negative maxConcurrentShardRequests", func(t *testing.T) {
+ server := mockElasticsearchServer()
+ defer server.Close()
+
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: -10,
@@ -149,6 +197,7 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
+ URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -158,6 +207,9 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("float maxConcurrentShardRequests", func(t *testing.T) {
+ server := mockElasticsearchServer()
+ defer server.Close()
+
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: 10.5,
@@ -166,6 +218,7 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
+ URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
@@ -175,6 +228,9 @@ func TestNewInstanceSettings(t *testing.T) {
})
t.Run("invalid maxConcurrentShardRequests", func(t *testing.T) {
+ server := mockElasticsearchServer()
+ defer server.Close()
+
dsInfo := datasourceInfo{
TimeField: "@timestamp",
MaxConcurrentShardRequests: "invalid",
@@ -183,6 +239,7 @@ func TestNewInstanceSettings(t *testing.T) {
require.NoError(t, err)
dsSettings := backend.DataSourceInstanceSettings{
+ URL: server.URL,
JSONData: json.RawMessage(settingsJSON),
}
diff --git a/pkg/tsdb/elasticsearch/healthcheck.go b/pkg/tsdb/elasticsearch/healthcheck.go
index 928945691de..cb5d0a866db 100644
--- a/pkg/tsdb/elasticsearch/healthcheck.go
+++ b/pkg/tsdb/elasticsearch/healthcheck.go
@@ -28,7 +28,6 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
Message: "Health check failed: Failed to get data source info",
}, nil
}
-
healthStatusUrl, err := url.Parse(ds.URL)
if err != nil {
logger.Error("Failed to parse data source URL", "error", err)
@@ -38,6 +37,14 @@ func (s *Service) CheckHealth(ctx context.Context, req *backend.CheckHealthReque
}, nil
}
+ // If the cluster is serverless, return a healthy result
+ if ds.ClusterInfo.IsServerless() {
+ return &backend.CheckHealthResult{
+ Status: backend.HealthStatusOk,
+ Message: "Elasticsearch Serverless data source is healthy.",
+ }, nil
+ }
+
// check that ES is healthy
healthStatusUrl.Path = path.Join(healthStatusUrl.Path, "_cluster/health")
healthStatusUrl.RawQuery = "wait_for_status=yellow"
diff --git a/pkg/tsdb/elasticsearch/metrics_response_processor.go b/pkg/tsdb/elasticsearch/metrics_response_processor.go
index 1e60a732d64..619180ccf90 100644
--- a/pkg/tsdb/elasticsearch/metrics_response_processor.go
+++ b/pkg/tsdb/elasticsearch/metrics_response_processor.go
@@ -9,7 +9,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
- "github.com/grafana/grafana/pkg/components/simplejson"
+ "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
// metricsResponseProcessor handles processing of metrics query responses
diff --git a/pkg/tsdb/elasticsearch/models.go b/pkg/tsdb/elasticsearch/models.go
index adb18554339..8df08182588 100644
--- a/pkg/tsdb/elasticsearch/models.go
+++ b/pkg/tsdb/elasticsearch/models.go
@@ -4,7 +4,7 @@ import (
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
- "github.com/grafana/grafana/pkg/components/simplejson"
+ "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
// Query represents the time series query model of the datasource
diff --git a/pkg/tsdb/elasticsearch/parse_query.go b/pkg/tsdb/elasticsearch/parse_query.go
index e1bfa189ab9..4d7b0cf7d5e 100644
--- a/pkg/tsdb/elasticsearch/parse_query.go
+++ b/pkg/tsdb/elasticsearch/parse_query.go
@@ -6,7 +6,7 @@ import (
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
- "github.com/grafana/grafana/pkg/components/simplejson"
+ "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
func parseQuery(tsdbQuery []backend.DataQuery, logger log.Logger) ([]*Query, error) {
diff --git a/pkg/tsdb/elasticsearch/raw_dsl_aggregation_parser.go b/pkg/tsdb/elasticsearch/raw_dsl_aggregation_parser.go
index b092763b57d..a569c92e7db 100644
--- a/pkg/tsdb/elasticsearch/raw_dsl_aggregation_parser.go
+++ b/pkg/tsdb/elasticsearch/raw_dsl_aggregation_parser.go
@@ -5,7 +5,7 @@ import (
"fmt"
"strconv"
- "github.com/grafana/grafana/pkg/components/simplejson"
+ "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
// AggregationParser parses raw Elasticsearch DSL aggregations
diff --git a/pkg/tsdb/elasticsearch/response_parser.go b/pkg/tsdb/elasticsearch/response_parser.go
index d05ca92e19b..2c0c5d33810 100644
--- a/pkg/tsdb/elasticsearch/response_parser.go
+++ b/pkg/tsdb/elasticsearch/response_parser.go
@@ -15,9 +15,9 @@ import (
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
- "github.com/grafana/grafana/pkg/components/simplejson"
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
"github.com/grafana/grafana/pkg/tsdb/elasticsearch/instrumentation"
+ "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
const (
diff --git a/pkg/tsdb/elasticsearch/response_utils.go b/pkg/tsdb/elasticsearch/response_utils.go
index c101633d0c2..5dfd1f38360 100644
--- a/pkg/tsdb/elasticsearch/response_utils.go
+++ b/pkg/tsdb/elasticsearch/response_utils.go
@@ -7,8 +7,8 @@ import (
"strings"
"time"
- "github.com/grafana/grafana/pkg/components/simplejson"
es "github.com/grafana/grafana/pkg/tsdb/elasticsearch/client"
+ "github.com/grafana/grafana/pkg/tsdb/elasticsearch/simplejson"
)
// flatten flattens multi-level objects to single level objects. It uses dot notation to join keys.
diff --git a/pkg/tsdb/elasticsearch/simplejson/simplejson.go b/pkg/tsdb/elasticsearch/simplejson/simplejson.go
new file mode 100644
index 00000000000..d7759ac3c2b
--- /dev/null
+++ b/pkg/tsdb/elasticsearch/simplejson/simplejson.go
@@ -0,0 +1,582 @@
+// Package simplejson provides a wrapper for arbitrary JSON objects that adds methods to access properties.
+// Use of this package in place of types and the standard library's encoding/json package is strongly discouraged.
+//
+// Don't lint for stale code, since it's a copied library and we might as well keep the whole thing.
+// nolint:unused
+package simplejson
+
+import (
+ "bytes"
+ "database/sql/driver"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "log"
+)
+
+// returns the current implementation version
+func Version() string {
+ return "0.5.0"
+}
+
+type Json struct {
+ data any
+}
+
+func (j *Json) FromDB(data []byte) error {
+ j.data = make(map[string]any)
+
+ dec := json.NewDecoder(bytes.NewBuffer(data))
+ dec.UseNumber()
+ return dec.Decode(&j.data)
+}
+
+func (j *Json) ToDB() ([]byte, error) {
+ if j == nil || j.data == nil {
+ return nil, nil
+ }
+
+ return j.Encode()
+}
+
+func (j *Json) Scan(val any) error {
+ switch v := val.(type) {
+ case []byte:
+ if len(v) == 0 {
+ return nil
+ }
+ return json.Unmarshal(v, &j)
+ case string:
+ if len(v) == 0 {
+ return nil
+ }
+ return json.Unmarshal([]byte(v), &j)
+ default:
+ return fmt.Errorf("unsupported type: %T", v)
+ }
+}
+
+func (j *Json) Value() (driver.Value, error) {
+ return j.ToDB()
+}
+
+// DeepCopyInto creates a copy by serializing JSON
+func (j *Json) DeepCopyInto(out *Json) {
+ b, err := j.Encode()
+ if err == nil {
+ _ = out.UnmarshalJSON(b)
+ }
+}
+
+// DeepCopy will make a deep copy of the JSON object
+func (j *Json) DeepCopy() *Json {
+ if j == nil {
+ return nil
+ }
+ out := new(Json)
+ j.DeepCopyInto(out)
+ return out
+}
+
+// NewJson returns a pointer to a new `Json` object
+// after unmarshaling `body` bytes
+func NewJson(body []byte) (*Json, error) {
+ j := new(Json)
+ err := j.UnmarshalJSON(body)
+ if err != nil {
+ return nil, err
+ }
+ return j, nil
+}
+
+// MustJson returns a pointer to a new `Json` object, panicking if `body` cannot be parsed.
+func MustJson(body []byte) *Json {
+ j, err := NewJson(body)
+
+ if err != nil {
+ panic(fmt.Sprintf("could not unmarshal JSON: %q", err))
+ }
+
+ return j
+}
+
+// New returns a pointer to a new, empty `Json` object
+func New() *Json {
+ return &Json{
+ data: make(map[string]any),
+ }
+}
+
+// NewFromAny returns a pointer to a new `Json` object with provided data.
+func NewFromAny(data any) *Json {
+ return &Json{data: data}
+}
+
+// Interface returns the underlying data
+func (j *Json) Interface() any {
+ return j.data
+}
+
+// Encode returns its marshaled data as `[]byte`
+func (j *Json) Encode() ([]byte, error) {
+ return j.MarshalJSON()
+}
+
+// EncodePretty returns its marshaled data as `[]byte` with indentation
+func (j *Json) EncodePretty() ([]byte, error) {
+ return json.MarshalIndent(&j.data, "", " ")
+}
+
+// Implements the json.Marshaler interface.
+func (j *Json) MarshalJSON() ([]byte, error) {
+ return json.Marshal(&j.data)
+}
+
+// Set modifies `Json` map by `key` and `value`
+// Useful for changing single key/value in a `Json` object easily.
+func (j *Json) Set(key string, val any) {
+ m, err := j.Map()
+ if err != nil {
+ return
+ }
+ m[key] = val
+}
+
+// SetPath modifies `Json`, recursively checking/creating map keys for the supplied path,
+// and then finally writing in the value
+func (j *Json) SetPath(branch []string, val any) {
+ if len(branch) == 0 {
+ j.data = val
+ return
+ }
+
+ // in order to insert our branch, we need map[string]any
+ if _, ok := (j.data).(map[string]any); !ok {
+ // have to replace with something suitable
+ j.data = make(map[string]any)
+ }
+ curr := j.data.(map[string]any)
+
+ for i := 0; i < len(branch)-1; i++ {
+ b := branch[i]
+ // key exists?
+ if _, ok := curr[b]; !ok {
+ n := make(map[string]any)
+ curr[b] = n
+ curr = n
+ continue
+ }
+
+ // make sure the value is the right sort of thing
+ if _, ok := curr[b].(map[string]any); !ok {
+ // have to replace with something suitable
+ n := make(map[string]any)
+ curr[b] = n
+ }
+
+ curr = curr[b].(map[string]any)
+ }
+
+ // add remaining k/v
+ curr[branch[len(branch)-1]] = val
+}
+
+// Del modifies `Json` map by deleting `key` if it is present.
+func (j *Json) Del(key string) {
+ m, err := j.Map()
+ if err != nil {
+ return
+ }
+ delete(m, key)
+}
+
+// Get returns a pointer to a new `Json` object
+// for `key` in its `map` representation
+//
+// useful for chaining operations (to traverse a nested JSON):
+//
+// js.Get("top_level").Get("dict").Get("value").Int()
+func (j *Json) Get(key string) *Json {
+ m, err := j.Map()
+ if err == nil {
+ if val, ok := m[key]; ok {
+ return &Json{val}
+ }
+ }
+ return &Json{nil}
+}
+
+// GetPath searches for the item as specified by the branch
+// without the need to deep dive using Get()'s.
+//
+// js.GetPath("top_level", "dict")
+func (j *Json) GetPath(branch ...string) *Json {
+ jin := j
+ for _, p := range branch {
+ jin = jin.Get(p)
+ }
+ return jin
+}
+
+// GetIndex returns a pointer to a new `Json` object
+// for `index` in its `array` representation
+//
+// this is the analog to Get when accessing elements of
+// a json array instead of a json object:
+//
+// js.Get("top_level").Get("array").GetIndex(1).Get("key").Int()
+func (j *Json) GetIndex(index int) *Json {
+ a, err := j.Array()
+ if err == nil {
+ if len(a) > index {
+ return &Json{a[index]}
+ }
+ }
+ return &Json{nil}
+}
+
+// CheckGetIndex returns a pointer to a new `Json` object
+// for `index` in its `array` representation, and a `bool`
+// indicating success or failure
+//
+// useful for chained operations when success is important:
+//
+// if data, ok := js.Get("top_level").CheckGetIndex(0); ok {
+// log.Println(data)
+// }
+func (j *Json) CheckGetIndex(index int) (*Json, bool) {
+ a, err := j.Array()
+ if err == nil {
+ if len(a) > index {
+ return &Json{a[index]}, true
+ }
+ }
+ return nil, false
+}
+
+// SetIndex modifies `Json` array by `index` and `value`
+// for `index` in its `array` representation
+func (j *Json) SetIndex(index int, val any) {
+ a, err := j.Array()
+ if err == nil {
+ if len(a) > index {
+ a[index] = val
+ }
+ }
+}
+
+// CheckGet returns a pointer to a new `Json` object and
+// a `bool` identifying success or failure
+//
+// useful for chained operations when success is important:
+//
+// if data, ok := js.Get("top_level").CheckGet("inner"); ok {
+// log.Println(data)
+// }
+func (j *Json) CheckGet(key string) (*Json, bool) {
+ m, err := j.Map()
+ if err == nil {
+ if val, ok := m[key]; ok {
+ return &Json{val}, true
+ }
+ }
+ return nil, false
+}
+
+// Map type asserts to `map`
+func (j *Json) Map() (map[string]any, error) {
+ if m, ok := (j.data).(map[string]any); ok {
+ return m, nil
+ }
+ return nil, errors.New("type assertion to map[string]any failed")
+}
+
+// Array type asserts to an `array`
+func (j *Json) Array() ([]any, error) {
+ if a, ok := (j.data).([]any); ok {
+ return a, nil
+ }
+ return nil, errors.New("type assertion to []any failed")
+}
+
+// Bool type asserts to `bool`
+func (j *Json) Bool() (bool, error) {
+ if s, ok := (j.data).(bool); ok {
+ return s, nil
+ }
+ return false, errors.New("type assertion to bool failed")
+}
+
+// String type asserts to `string`
+func (j *Json) String() (string, error) {
+ if s, ok := (j.data).(string); ok {
+ return s, nil
+ }
+ return "", errors.New("type assertion to string failed")
+}
+
+// Bytes type asserts to `[]byte`
+func (j *Json) Bytes() ([]byte, error) {
+ if s, ok := (j.data).(string); ok {
+ return []byte(s), nil
+ }
+ return nil, errors.New("type assertion to []byte failed")
+}
+
+// StringArray type asserts to an `array` of `string`
+func (j *Json) StringArray() ([]string, error) {
+ arr, err := j.Array()
+ if err != nil {
+ return nil, err
+ }
+ retArr := make([]string, 0, len(arr))
+ for _, a := range arr {
+ if a == nil {
+ retArr = append(retArr, "")
+ continue
+ }
+ s, ok := a.(string)
+ if !ok {
+ return nil, err
+ }
+ retArr = append(retArr, s)
+ }
+ return retArr, nil
+}
+
+// MustArray guarantees the return of a `[]any` (with optional default)
+//
+// useful when you want to iterate over array values in a succinct manner:
+//
+// for i, v := range js.Get("results").MustArray() {
+// fmt.Println(i, v)
+// }
+func (j *Json) MustArray(args ...[]any) []any {
+ var def []any
+
+ switch len(args) {
+ case 0:
+ case 1:
+ def = args[0]
+ default:
+ log.Panicf("MustArray() received too many arguments %d", len(args))
+ }
+
+ a, err := j.Array()
+ if err == nil {
+ return a
+ }
+
+ return def
+}
+
+// MustMap guarantees the return of a `map[string]any` (with optional default)
+//
+// useful when you want to iterate over map values in a succinct manner:
+//
+// for k, v := range js.Get("dictionary").MustMap() {
+// fmt.Println(k, v)
+// }
+func (j *Json) MustMap(args ...map[string]any) map[string]any {
+ var def map[string]any
+
+ switch len(args) {
+ case 0:
+ case 1:
+ def = args[0]
+ default:
+ log.Panicf("MustMap() received too many arguments %d", len(args))
+ }
+
+ a, err := j.Map()
+ if err == nil {
+ return a
+ }
+
+ return def
+}
+
+// MustString guarantees the return of a `string` (with optional default)
+//
+// useful when you explicitly want a `string` in a single value return context:
+//
+// myFunc(js.Get("param1").MustString(), js.Get("optional_param").MustString("my_default"))
+func (j *Json) MustString(args ...string) string {
+ var def string
+
+ switch len(args) {
+ case 0:
+ case 1:
+ def = args[0]
+ default:
+ log.Panicf("MustString() received too many arguments %d", len(args))
+ }
+
+ s, err := j.String()
+ if err == nil {
+ return s
+ }
+
+ return def
+}
+
+// MustStringArray guarantees the return of a `[]string` (with optional default)
+//
+// useful when you want to iterate over array values in a succinct manner:
+//
+// for i, s := range js.Get("results").MustStringArray() {
+// fmt.Println(i, s)
+// }
+func (j *Json) MustStringArray(args ...[]string) []string {
+ var def []string
+
+ switch len(args) {
+ case 0:
+ case 1:
+ def = args[0]
+ default:
+ log.Panicf("MustStringArray() received too many arguments %d", len(args))
+ }
+
+ a, err := j.StringArray()
+ if err == nil {
+ return a
+ }
+
+ return def
+}
+
+// MustInt guarantees the return of an `int` (with optional default)
+//
+// useful when you explicitly want an `int` in a single value return context:
+//
+// myFunc(js.Get("param1").MustInt(), js.Get("optional_param").MustInt(5150))
+func (j *Json) MustInt(args ...int) int {
+ var def int
+
+ switch len(args) {
+ case 0:
+ case 1:
+ def = args[0]
+ default:
+ log.Panicf("MustInt() received too many arguments %d", len(args))
+ }
+
+ i, err := j.Int()
+ if err == nil {
+ return i
+ }
+
+ return def
+}
+
+// MustFloat64 guarantees the return of a `float64` (with optional default)
+//
+// useful when you explicitly want a `float64` in a single value return context:
+//
+// myFunc(js.Get("param1").MustFloat64(), js.Get("optional_param").MustFloat64(5.150))
+func (j *Json) MustFloat64(args ...float64) float64 {
+ var def float64
+
+ switch len(args) {
+ case 0:
+ case 1:
+ def = args[0]
+ default:
+ log.Panicf("MustFloat64() received too many arguments %d", len(args))
+ }
+
+ f, err := j.Float64()
+ if err == nil {
+ return f
+ }
+
+ return def
+}
+
+// MustBool guarantees the return of a `bool` (with optional default)
+//
+// useful when you explicitly want a `bool` in a single value return context:
+//
+// myFunc(js.Get("param1").MustBool(), js.Get("optional_param").MustBool(true))
+func (j *Json) MustBool(args ...bool) bool {
+ var def bool
+
+ switch len(args) {
+ case 0:
+ case 1:
+ def = args[0]
+ default:
+ log.Panicf("MustBool() received too many arguments %d", len(args))
+ }
+
+ b, err := j.Bool()
+ if err == nil {
+ return b
+ }
+
+ return def
+}
+
+// MustInt64 guarantees the return of an `int64` (with optional default)
+//
+// useful when you explicitly want an `int64` in a single value return context:
+//
+// myFunc(js.Get("param1").MustInt64(), js.Get("optional_param").MustInt64(5150))
+func (j *Json) MustInt64(args ...int64) int64 {
+ var def int64
+
+ switch len(args) {
+ case 0:
+ case 1:
+ def = args[0]
+ default:
+ log.Panicf("MustInt64() received too many arguments %d", len(args))
+ }
+
+ i, err := j.Int64()
+ if err == nil {
+ return i
+ }
+
+ return def
+}
+
+// MustUInt64 guarantees the return of an `uint64` (with optional default)
+//
+// useful when you explicitly want an `uint64` in a single value return context:
+//
+// myFunc(js.Get("param1").MustUint64(), js.Get("optional_param").MustUint64(5150))
+func (j *Json) MustUint64(args ...uint64) uint64 {
+ var def uint64
+
+ switch len(args) {
+ case 0:
+ case 1:
+ def = args[0]
+ default:
+ log.Panicf("MustUint64() received too many arguments %d", len(args))
+ }
+
+ i, err := j.Uint64()
+ if err == nil {
+ return i
+ }
+
+ return def
+}
+
+// MarshalYAML implements yaml.Marshaller.
+func (j *Json) MarshalYAML() (any, error) {
+ return j.data, nil
+}
+
+// UnmarshalYAML implements yaml.Unmarshaller.
+func (j *Json) UnmarshalYAML(unmarshal func(any) error) error {
+ var data any
+ if err := unmarshal(&data); err != nil {
+ return err
+ }
+ j.data = data
+ return nil
+}
diff --git a/pkg/tsdb/elasticsearch/simplejson/simplejson_go11.go b/pkg/tsdb/elasticsearch/simplejson/simplejson_go11.go
new file mode 100644
index 00000000000..88748985576
--- /dev/null
+++ b/pkg/tsdb/elasticsearch/simplejson/simplejson_go11.go
@@ -0,0 +1,90 @@
+package simplejson
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "io"
+ "reflect"
+ "strconv"
+)
+
+// Implements the json.Unmarshaler interface.
+func (j *Json) UnmarshalJSON(p []byte) error {
+ dec := json.NewDecoder(bytes.NewBuffer(p))
+ dec.UseNumber()
+ return dec.Decode(&j.data)
+}
+
+// NewFromReader returns a *Json by decoding from an io.Reader
+func NewFromReader(r io.Reader) (*Json, error) {
+ j := new(Json)
+ dec := json.NewDecoder(r)
+ dec.UseNumber()
+ err := dec.Decode(&j.data)
+ return j, err
+}
+
+// Float64 coerces into a float64
+func (j *Json) Float64() (float64, error) {
+ switch n := j.data.(type) {
+ case json.Number:
+ return n.Float64()
+ case float32, float64:
+ return reflect.ValueOf(j.data).Float(), nil
+ case int, int8, int16, int32, int64:
+ return float64(reflect.ValueOf(j.data).Int()), nil
+ case uint, uint8, uint16, uint32, uint64:
+ return float64(reflect.ValueOf(j.data).Uint()), nil
+ }
+ return 0, errors.New("invalid value type")
+}
+
+// Int coerces into an int
+func (j *Json) Int() (int, error) {
+ switch n := j.data.(type) {
+ case json.Number:
+ i, err := n.Int64()
+ if err != nil {
+ return 0, err
+ }
+ return int(i), nil
+ case float32, float64:
+ return int(reflect.ValueOf(j.data).Float()), nil
+ case int, int8, int16, int32, int64:
+ return int(reflect.ValueOf(j.data).Int()), nil
+ case uint, uint8, uint16, uint32, uint64:
+ return int(reflect.ValueOf(j.data).Uint()), nil
+ }
+ return 0, errors.New("invalid value type")
+}
+
+// Int64 coerces into an int64
+func (j *Json) Int64() (int64, error) {
+ switch n := j.data.(type) {
+ case json.Number:
+ return n.Int64()
+ case float32, float64:
+ return int64(reflect.ValueOf(j.data).Float()), nil
+ case int, int8, int16, int32, int64:
+ return reflect.ValueOf(j.data).Int(), nil
+ case uint, uint8, uint16, uint32, uint64:
+ return int64(reflect.ValueOf(j.data).Uint()), nil
+ }
+ return 0, errors.New("invalid value type")
+}
+
+// Uint64 coerces into an uint64
+func (j *Json) Uint64() (uint64, error) {
+ switch n := j.data.(type) {
+ case json.Number:
+ return strconv.ParseUint(n.String(), 10, 64)
+ case float32, float64:
+ return uint64(reflect.ValueOf(j.data).Float()), nil
+ case int, int8, int16, int32, int64:
+ return uint64(reflect.ValueOf(j.data).Int()), nil
+ case uint, uint8, uint16, uint32, uint64:
+ return reflect.ValueOf(j.data).Uint(), nil
+ }
+ return 0, errors.New("invalid value type")
+}
diff --git a/pkg/tsdb/elasticsearch/simplejson/simplejson_test.go b/pkg/tsdb/elasticsearch/simplejson/simplejson_test.go
new file mode 100644
index 00000000000..efc786bc745
--- /dev/null
+++ b/pkg/tsdb/elasticsearch/simplejson/simplejson_test.go
@@ -0,0 +1,274 @@
+package simplejson
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSimplejson(t *testing.T) {
+ var ok bool
+ var err error
+
+ js, err := NewJson([]byte(`{
+ "test": {
+ "string_array": ["asdf", "ghjk", "zxcv"],
+ "string_array_null": ["abc", null, "efg"],
+ "array": [1, "2", 3],
+ "arraywithsubs": [{"subkeyone": 1},
+ {"subkeytwo": 2, "subkeythree": 3}],
+ "int": 10,
+ "float": 5.150,
+ "string": "simplejson",
+ "bool": true,
+ "sub_obj": {"a": 1}
+ }
+ }`))
+
+ assert.NotEqual(t, nil, js)
+ assert.Equal(t, nil, err)
+
+ _, ok = js.CheckGet("test")
+ assert.Equal(t, true, ok)
+
+ _, ok = js.CheckGet("missing_key")
+ assert.Equal(t, false, ok)
+
+ aws := js.Get("test").Get("arraywithsubs")
+ assert.NotEqual(t, nil, aws)
+ var awsval int
+ awsval, _ = aws.GetIndex(0).Get("subkeyone").Int()
+ assert.Equal(t, 1, awsval)
+ awsval, _ = aws.GetIndex(1).Get("subkeytwo").Int()
+ assert.Equal(t, 2, awsval)
+ awsval, _ = aws.GetIndex(1).Get("subkeythree").Int()
+ assert.Equal(t, 3, awsval)
+
+ arr := js.Get("test").Get("array")
+ assert.NotEqual(t, nil, arr)
+ val, ok := arr.CheckGetIndex(0)
+ assert.Equal(t, ok, true)
+ valInt, _ := val.Int()
+ assert.Equal(t, valInt, 1)
+ val, ok = arr.CheckGetIndex(1)
+ assert.Equal(t, ok, true)
+ valStr, _ := val.String()
+ assert.Equal(t, valStr, "2")
+ val, ok = arr.CheckGetIndex(2)
+ assert.Equal(t, ok, true)
+ valInt, _ = val.Int()
+ assert.Equal(t, valInt, 3)
+ _, ok = arr.CheckGetIndex(3)
+ assert.Equal(t, ok, false)
+
+ i, _ := js.Get("test").Get("int").Int()
+ assert.Equal(t, 10, i)
+
+ f, _ := js.Get("test").Get("float").Float64()
+ assert.Equal(t, 5.150, f)
+
+ s, _ := js.Get("test").Get("string").String()
+ assert.Equal(t, "simplejson", s)
+
+ b, _ := js.Get("test").Get("bool").Bool()
+ assert.Equal(t, true, b)
+
+ mi := js.Get("test").Get("int").MustInt()
+ assert.Equal(t, 10, mi)
+
+ mi2 := js.Get("test").Get("missing_int").MustInt(5150)
+ assert.Equal(t, 5150, mi2)
+
+ ms := js.Get("test").Get("string").MustString()
+ assert.Equal(t, "simplejson", ms)
+
+ ms2 := js.Get("test").Get("missing_string").MustString("fyea")
+ assert.Equal(t, "fyea", ms2)
+
+ ma2 := js.Get("test").Get("missing_array").MustArray([]any{"1", 2, "3"})
+ assert.Equal(t, ma2, []any{"1", 2, "3"})
+
+ msa := js.Get("test").Get("string_array").MustStringArray()
+ assert.Equal(t, msa[0], "asdf")
+ assert.Equal(t, msa[1], "ghjk")
+ assert.Equal(t, msa[2], "zxcv")
+
+ msa2 := js.Get("test").Get("string_array").MustStringArray([]string{"1", "2", "3"})
+ assert.Equal(t, msa2[0], "asdf")
+ assert.Equal(t, msa2[1], "ghjk")
+ assert.Equal(t, msa2[2], "zxcv")
+
+ msa3 := js.Get("test").Get("missing_array").MustStringArray([]string{"1", "2", "3"})
+ assert.Equal(t, msa3, []string{"1", "2", "3"})
+
+ mm2 := js.Get("test").Get("missing_map").MustMap(map[string]any{"found": false})
+ assert.Equal(t, mm2, map[string]any{"found": false})
+
+ strs, err := js.Get("test").Get("string_array").StringArray()
+ assert.Equal(t, err, nil)
+ assert.Equal(t, strs[0], "asdf")
+ assert.Equal(t, strs[1], "ghjk")
+ assert.Equal(t, strs[2], "zxcv")
+
+ strs2, err := js.Get("test").Get("string_array_null").StringArray()
+ assert.Equal(t, err, nil)
+ assert.Equal(t, strs2[0], "abc")
+ assert.Equal(t, strs2[1], "")
+ assert.Equal(t, strs2[2], "efg")
+
+ gp, _ := js.GetPath("test", "string").String()
+ assert.Equal(t, "simplejson", gp)
+
+ gp2, _ := js.GetPath("test", "int").Int()
+ assert.Equal(t, 10, gp2)
+
+ assert.Equal(t, js.Get("test").Get("bool").MustBool(), true)
+
+ js.Set("float2", 300.0)
+ assert.Equal(t, js.Get("float2").MustFloat64(), 300.0)
+
+ js.Set("test2", "setTest")
+ assert.Equal(t, "setTest", js.Get("test2").MustString())
+
+ js.Del("test2")
+ assert.NotEqual(t, "setTest", js.Get("test2").MustString())
+
+ js.Get("test").Get("sub_obj").Set("a", 2)
+ assert.Equal(t, 2, js.Get("test").Get("sub_obj").Get("a").MustInt())
+
+ js.GetPath("test", "sub_obj").Set("a", 3)
+ assert.Equal(t, 3, js.GetPath("test", "sub_obj", "a").MustInt())
+}
+
+func TestStdlibInterfaces(t *testing.T) {
+ val := new(struct {
+ Name string `json:"name"`
+ Params *Json `json:"params"`
+ })
+ val2 := new(struct {
+ Name string `json:"name"`
+ Params *Json `json:"params"`
+ })
+
+ raw := `{"name":"myobject","params":{"string":"simplejson"}}`
+
+ assert.Equal(t, nil, json.Unmarshal([]byte(raw), val))
+
+ assert.Equal(t, "myobject", val.Name)
+ assert.NotEqual(t, nil, val.Params.data)
+ s, _ := val.Params.Get("string").String()
+ assert.Equal(t, "simplejson", s)
+
+ p, err := json.Marshal(val)
+ assert.Equal(t, nil, err)
+ assert.Equal(t, nil, json.Unmarshal(p, val2))
+ assert.Equal(t, val, val2) // stable
+}
+
+func TestSet(t *testing.T) {
+ js, err := NewJson([]byte(`{}`))
+ assert.Equal(t, nil, err)
+
+ js.Set("baz", "bing")
+
+ s, err := js.GetPath("baz").String()
+ assert.Equal(t, nil, err)
+ assert.Equal(t, "bing", s)
+}
+
+func TestReplace(t *testing.T) {
+ js, err := NewJson([]byte(`{}`))
+ assert.Equal(t, nil, err)
+
+ err = js.UnmarshalJSON([]byte(`{"baz":"bing"}`))
+ assert.Equal(t, nil, err)
+
+ s, err := js.GetPath("baz").String()
+ assert.Equal(t, nil, err)
+ assert.Equal(t, "bing", s)
+}
+
+func TestSetPath(t *testing.T) {
+ js, err := NewJson([]byte(`{}`))
+ assert.Equal(t, nil, err)
+
+ js.SetPath([]string{"foo", "bar"}, "baz")
+
+ s, err := js.GetPath("foo", "bar").String()
+ assert.Equal(t, nil, err)
+ assert.Equal(t, "baz", s)
+}
+
+func TestSetPathNoPath(t *testing.T) {
+ js, err := NewJson([]byte(`{"some":"data","some_number":1.0,"some_bool":false}`))
+ assert.Equal(t, nil, err)
+
+ f := js.GetPath("some_number").MustFloat64(99.0)
+ assert.Equal(t, f, 1.0)
+
+ js.SetPath([]string{}, map[string]any{"foo": "bar"})
+
+ s, err := js.GetPath("foo").String()
+ assert.Equal(t, nil, err)
+ assert.Equal(t, "bar", s)
+
+ f = js.GetPath("some_number").MustFloat64(99.0)
+ assert.Equal(t, f, 99.0)
+}
+
+func TestPathWillAugmentExisting(t *testing.T) {
+ js, err := NewJson([]byte(`{"this":{"a":"aa","b":"bb","c":"cc"}}`))
+ assert.Equal(t, nil, err)
+
+ js.SetPath([]string{"this", "d"}, "dd")
+
+ cases := []struct {
+ path []string
+ outcome string
+ }{
+ {
+ path: []string{"this", "a"},
+ outcome: "aa",
+ },
+ {
+ path: []string{"this", "b"},
+ outcome: "bb",
+ },
+ {
+ path: []string{"this", "c"},
+ outcome: "cc",
+ },
+ {
+ path: []string{"this", "d"},
+ outcome: "dd",
+ },
+ }
+
+ for _, tc := range cases {
+ s, err := js.GetPath(tc.path...).String()
+ assert.Equal(t, nil, err)
+ assert.Equal(t, tc.outcome, s)
+ }
+}
+
+func TestPathWillOverwriteExisting(t *testing.T) {
+ // notice how "a" is 0.1 - but then we'll try to set at path a, foo
+ js, err := NewJson([]byte(`{"this":{"a":0.1,"b":"bb","c":"cc"}}`))
+ assert.Equal(t, nil, err)
+
+ js.SetPath([]string{"this", "a", "foo"}, "bar")
+
+ s, err := js.GetPath("this", "a", "foo").String()
+ assert.Equal(t, nil, err)
+ assert.Equal(t, "bar", s)
+}
+
+func TestMustJson(t *testing.T) {
+ js := MustJson([]byte(`{"foo": "bar"}`))
+ assert.Equal(t, js.Get("foo").MustString(), "bar")
+
+ assert.PanicsWithValue(t, "could not unmarshal JSON: \"unexpected EOF\"", func() {
+ MustJson([]byte(`{`))
+ })
+}
diff --git a/pkg/tsdb/elasticsearch/standalone/datasource.go b/pkg/tsdb/elasticsearch/standalone/datasource.go
new file mode 100644
index 00000000000..6b9b8ac3f82
--- /dev/null
+++ b/pkg/tsdb/elasticsearch/standalone/datasource.go
@@ -0,0 +1,48 @@
+package main
+
+import (
+ "context"
+
+ "github.com/grafana/grafana-plugin-sdk-go/backend"
+ "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
+ "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
+ elasticsearch "github.com/grafana/grafana/pkg/tsdb/elasticsearch"
+)
+
+var (
+ _ backend.QueryDataHandler = (*Datasource)(nil)
+ _ backend.CheckHealthHandler = (*Datasource)(nil)
+ _ backend.CallResourceHandler = (*Datasource)(nil)
+)
+
+func NewDatasource(context.Context, backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
+ return &Datasource{
+ Service: elasticsearch.ProvideService(httpclient.NewProvider()),
+ }, nil
+}
+
+type Datasource struct {
+ Service *elasticsearch.Service
+}
+
+func contextualMiddlewares(ctx context.Context) context.Context {
+ cfg := backend.GrafanaConfigFromContext(ctx)
+ responseLimitMiddleware := httpclient.ResponseLimitMiddleware(cfg.ResponseLimit())
+ ctx = httpclient.WithContextualMiddleware(ctx, responseLimitMiddleware)
+ return ctx
+}
+
+func (d *Datasource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
+ ctx = contextualMiddlewares(ctx)
+ return d.Service.QueryData(ctx, req)
+}
+
+func (d *Datasource) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
+ ctx = contextualMiddlewares(ctx)
+ return d.Service.CallResource(ctx, req, sender)
+}
+
+func (d *Datasource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
+ ctx = contextualMiddlewares(ctx)
+ return d.Service.CheckHealth(ctx, req)
+}
diff --git a/pkg/tsdb/elasticsearch/standalone/main.go b/pkg/tsdb/elasticsearch/standalone/main.go
new file mode 100644
index 00000000000..22bd4169339
--- /dev/null
+++ b/pkg/tsdb/elasticsearch/standalone/main.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "os"
+
+ "github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
+ "github.com/grafana/grafana-plugin-sdk-go/backend/log"
+)
+
+func main() {
+ // Start listening to requests sent from Grafana. This call is blocking so
+ // it won't finish until Grafana shuts down the process or the plugin choose
+ // to exit by itself using os.Exit. Manage automatically manages life cycle
+ // of datasource instances. It accepts datasource instance factory as first
+ // argument. This factory will be automatically called on incoming request
+ // from Grafana to create different instances of SampleDatasource (per datasource
+ // ID). When datasource configuration changed Dispose method will be called and
+ // new datasource instance created using NewSampleDatasource factory.
+ if err := datasource.Manage("elasticsearch", NewDatasource, datasource.ManageOpts{}); err != nil {
+ log.DefaultLogger.Error(err.Error())
+ os.Exit(1)
+ }
+}
diff --git a/public/api-merged.json b/public/api-merged.json
index d511139c7bd..8dc5868bb41 100644
--- a/public/api-merged.json
+++ b/public/api-merged.json
@@ -4024,12 +4024,14 @@
},
"/dashboards/uid/{uid}/restore": {
"post": {
+ "description": "This API will be removed when /apis/dashboards.grafana.app/v1 is released.\nYou can restore a dashboard by reading it from history, then creating it again.",
"tags": [
"dashboards",
"versions"
],
"summary": "Restore a dashboard to a given dashboard version using UID.",
"operationId": "restoreDashboardVersionByUID",
+ "deprecated": true,
"parameters": [
{
"name": "Body",
diff --git a/public/app/api/clients/scope/v0alpha1/baseAPI.ts b/public/app/api/clients/scope/v0alpha1/baseAPI.ts
new file mode 100644
index 00000000000..bdec3014c1e
--- /dev/null
+++ b/public/app/api/clients/scope/v0alpha1/baseAPI.ts
@@ -0,0 +1,16 @@
+import { createApi } from '@reduxjs/toolkit/query/react';
+
+import { getAPIBaseURL } from '@grafana/api-clients';
+import { createBaseQuery } from '@grafana/api-clients/rtkq';
+
+export const API_GROUP = 'scope.grafana.app' as const;
+export const API_VERSION = 'v0alpha1' as const;
+export const BASE_URL = getAPIBaseURL(API_GROUP, API_VERSION);
+
+export const api = createApi({
+ reducerPath: 'scopeAPIv0alpha1',
+ baseQuery: createBaseQuery({
+ baseURL: BASE_URL,
+ }),
+ endpoints: () => ({}),
+});
diff --git a/public/app/api/clients/scope/v0alpha1/endpoints.gen.ts b/public/app/api/clients/scope/v0alpha1/endpoints.gen.ts
new file mode 100644
index 00000000000..0bd43fc2b05
--- /dev/null
+++ b/public/app/api/clients/scope/v0alpha1/endpoints.gen.ts
@@ -0,0 +1,1727 @@
+import { api } from './baseAPI';
+export const addTagTypes = [
+ 'API Discovery',
+ 'FindScopeDashboardBindingsResults',
+ 'FindScopeNavigationsResults',
+ 'FindScopeNodeChildrenResults',
+ 'ScopeDashboardBinding',
+ 'ScopeNavigation',
+ 'ScopeNode',
+ 'Scope',
+] as const;
+const injectedRtkApi = api
+ .enhanceEndpoints({
+ addTagTypes,
+ })
+ .injectEndpoints({
+ endpoints: (build) => ({
+ getApiResources: build.query({
+ query: () => ({ url: `/` }),
+ providesTags: ['API Discovery'],
+ }),
+ getFindScopeDashboardBindingsResults: build.query<
+ GetFindScopeDashboardBindingsResultsApiResponse,
+ GetFindScopeDashboardBindingsResultsApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/find/scope_dashboard_bindings`,
+ params: {
+ scope: queryArg.scope,
+ },
+ }),
+ providesTags: ['FindScopeDashboardBindingsResults'],
+ }),
+ getFindScopeNavigationsResults: build.query<
+ GetFindScopeNavigationsResultsApiResponse,
+ GetFindScopeNavigationsResultsApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/find/scope_navigations`,
+ params: {
+ scope: queryArg.scope,
+ },
+ }),
+ providesTags: ['FindScopeNavigationsResults'],
+ }),
+ getFindScopeNodeChildrenResults: build.query<
+ GetFindScopeNodeChildrenResultsApiResponse,
+ GetFindScopeNodeChildrenResultsApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/find/scope_node_children`,
+ params: {
+ parent: queryArg.parent,
+ query: queryArg.query,
+ names: queryArg.names,
+ limit: queryArg.limit,
+ },
+ }),
+ providesTags: ['FindScopeNodeChildrenResults'],
+ }),
+ listScopeDashboardBinding: build.query({
+ query: (queryArg) => ({
+ url: `/scopedashboardbindings`,
+ params: {
+ pretty: queryArg.pretty,
+ allowWatchBookmarks: queryArg.allowWatchBookmarks,
+ continue: queryArg['continue'],
+ fieldSelector: queryArg.fieldSelector,
+ labelSelector: queryArg.labelSelector,
+ limit: queryArg.limit,
+ resourceVersion: queryArg.resourceVersion,
+ resourceVersionMatch: queryArg.resourceVersionMatch,
+ sendInitialEvents: queryArg.sendInitialEvents,
+ timeoutSeconds: queryArg.timeoutSeconds,
+ watch: queryArg.watch,
+ },
+ }),
+ providesTags: ['ScopeDashboardBinding'],
+ }),
+ createScopeDashboardBinding: build.mutation<
+ CreateScopeDashboardBindingApiResponse,
+ CreateScopeDashboardBindingApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/scopedashboardbindings`,
+ method: 'POST',
+ body: queryArg.scopeDashboardBinding,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ },
+ }),
+ invalidatesTags: ['ScopeDashboardBinding'],
+ }),
+ deletecollectionScopeDashboardBinding: build.mutation<
+ DeletecollectionScopeDashboardBindingApiResponse,
+ DeletecollectionScopeDashboardBindingApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/scopedashboardbindings`,
+ method: 'DELETE',
+ params: {
+ pretty: queryArg.pretty,
+ continue: queryArg['continue'],
+ dryRun: queryArg.dryRun,
+ fieldSelector: queryArg.fieldSelector,
+ gracePeriodSeconds: queryArg.gracePeriodSeconds,
+ ignoreStoreReadErrorWithClusterBreakingPotential: queryArg.ignoreStoreReadErrorWithClusterBreakingPotential,
+ labelSelector: queryArg.labelSelector,
+ limit: queryArg.limit,
+ orphanDependents: queryArg.orphanDependents,
+ propagationPolicy: queryArg.propagationPolicy,
+ resourceVersion: queryArg.resourceVersion,
+ resourceVersionMatch: queryArg.resourceVersionMatch,
+ sendInitialEvents: queryArg.sendInitialEvents,
+ timeoutSeconds: queryArg.timeoutSeconds,
+ },
+ }),
+ invalidatesTags: ['ScopeDashboardBinding'],
+ }),
+ getScopeDashboardBinding: build.query({
+ query: (queryArg) => ({
+ url: `/scopedashboardbindings/${queryArg.name}`,
+ params: {
+ pretty: queryArg.pretty,
+ },
+ }),
+ providesTags: ['ScopeDashboardBinding'],
+ }),
+ replaceScopeDashboardBinding: build.mutation<
+ ReplaceScopeDashboardBindingApiResponse,
+ ReplaceScopeDashboardBindingApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/scopedashboardbindings/${queryArg.name}`,
+ method: 'PUT',
+ body: queryArg.scopeDashboardBinding,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ },
+ }),
+ invalidatesTags: ['ScopeDashboardBinding'],
+ }),
+ deleteScopeDashboardBinding: build.mutation<
+ DeleteScopeDashboardBindingApiResponse,
+ DeleteScopeDashboardBindingApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/scopedashboardbindings/${queryArg.name}`,
+ method: 'DELETE',
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ gracePeriodSeconds: queryArg.gracePeriodSeconds,
+ ignoreStoreReadErrorWithClusterBreakingPotential: queryArg.ignoreStoreReadErrorWithClusterBreakingPotential,
+ orphanDependents: queryArg.orphanDependents,
+ propagationPolicy: queryArg.propagationPolicy,
+ },
+ }),
+ invalidatesTags: ['ScopeDashboardBinding'],
+ }),
+ updateScopeDashboardBinding: build.mutation<
+ UpdateScopeDashboardBindingApiResponse,
+ UpdateScopeDashboardBindingApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/scopedashboardbindings/${queryArg.name}`,
+ method: 'PATCH',
+ body: queryArg.patch,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ force: queryArg.force,
+ },
+ }),
+ invalidatesTags: ['ScopeDashboardBinding'],
+ }),
+ getScopeDashboardBindingStatus: build.query<
+ GetScopeDashboardBindingStatusApiResponse,
+ GetScopeDashboardBindingStatusApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/scopedashboardbindings/${queryArg.name}/status`,
+ params: {
+ pretty: queryArg.pretty,
+ },
+ }),
+ providesTags: ['ScopeDashboardBinding'],
+ }),
+ replaceScopeDashboardBindingStatus: build.mutation<
+ ReplaceScopeDashboardBindingStatusApiResponse,
+ ReplaceScopeDashboardBindingStatusApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/scopedashboardbindings/${queryArg.name}/status`,
+ method: 'PUT',
+ body: queryArg.scopeDashboardBinding,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ },
+ }),
+ invalidatesTags: ['ScopeDashboardBinding'],
+ }),
+ updateScopeDashboardBindingStatus: build.mutation<
+ UpdateScopeDashboardBindingStatusApiResponse,
+ UpdateScopeDashboardBindingStatusApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/scopedashboardbindings/${queryArg.name}/status`,
+ method: 'PATCH',
+ body: queryArg.patch,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ force: queryArg.force,
+ },
+ }),
+ invalidatesTags: ['ScopeDashboardBinding'],
+ }),
+ listScopeNavigation: build.query({
+ query: (queryArg) => ({
+ url: `/scopenavigations`,
+ params: {
+ pretty: queryArg.pretty,
+ allowWatchBookmarks: queryArg.allowWatchBookmarks,
+ continue: queryArg['continue'],
+ fieldSelector: queryArg.fieldSelector,
+ labelSelector: queryArg.labelSelector,
+ limit: queryArg.limit,
+ resourceVersion: queryArg.resourceVersion,
+ resourceVersionMatch: queryArg.resourceVersionMatch,
+ sendInitialEvents: queryArg.sendInitialEvents,
+ timeoutSeconds: queryArg.timeoutSeconds,
+ watch: queryArg.watch,
+ },
+ }),
+ providesTags: ['ScopeNavigation'],
+ }),
+ createScopeNavigation: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopenavigations`,
+ method: 'POST',
+ body: queryArg.scopeNavigation,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ },
+ }),
+ invalidatesTags: ['ScopeNavigation'],
+ }),
+ deletecollectionScopeNavigation: build.mutation<
+ DeletecollectionScopeNavigationApiResponse,
+ DeletecollectionScopeNavigationApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/scopenavigations`,
+ method: 'DELETE',
+ params: {
+ pretty: queryArg.pretty,
+ continue: queryArg['continue'],
+ dryRun: queryArg.dryRun,
+ fieldSelector: queryArg.fieldSelector,
+ gracePeriodSeconds: queryArg.gracePeriodSeconds,
+ ignoreStoreReadErrorWithClusterBreakingPotential: queryArg.ignoreStoreReadErrorWithClusterBreakingPotential,
+ labelSelector: queryArg.labelSelector,
+ limit: queryArg.limit,
+ orphanDependents: queryArg.orphanDependents,
+ propagationPolicy: queryArg.propagationPolicy,
+ resourceVersion: queryArg.resourceVersion,
+ resourceVersionMatch: queryArg.resourceVersionMatch,
+ sendInitialEvents: queryArg.sendInitialEvents,
+ timeoutSeconds: queryArg.timeoutSeconds,
+ },
+ }),
+ invalidatesTags: ['ScopeNavigation'],
+ }),
+ getScopeNavigation: build.query({
+ query: (queryArg) => ({
+ url: `/scopenavigations/${queryArg.name}`,
+ params: {
+ pretty: queryArg.pretty,
+ },
+ }),
+ providesTags: ['ScopeNavigation'],
+ }),
+ replaceScopeNavigation: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopenavigations/${queryArg.name}`,
+ method: 'PUT',
+ body: queryArg.scopeNavigation,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ },
+ }),
+ invalidatesTags: ['ScopeNavigation'],
+ }),
+ deleteScopeNavigation: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopenavigations/${queryArg.name}`,
+ method: 'DELETE',
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ gracePeriodSeconds: queryArg.gracePeriodSeconds,
+ ignoreStoreReadErrorWithClusterBreakingPotential: queryArg.ignoreStoreReadErrorWithClusterBreakingPotential,
+ orphanDependents: queryArg.orphanDependents,
+ propagationPolicy: queryArg.propagationPolicy,
+ },
+ }),
+ invalidatesTags: ['ScopeNavigation'],
+ }),
+ updateScopeNavigation: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopenavigations/${queryArg.name}`,
+ method: 'PATCH',
+ body: queryArg.patch,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ force: queryArg.force,
+ },
+ }),
+ invalidatesTags: ['ScopeNavigation'],
+ }),
+ getScopeNavigationStatus: build.query({
+ query: (queryArg) => ({
+ url: `/scopenavigations/${queryArg.name}/status`,
+ params: {
+ pretty: queryArg.pretty,
+ },
+ }),
+ providesTags: ['ScopeNavigation'],
+ }),
+ replaceScopeNavigationStatus: build.mutation<
+ ReplaceScopeNavigationStatusApiResponse,
+ ReplaceScopeNavigationStatusApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/scopenavigations/${queryArg.name}/status`,
+ method: 'PUT',
+ body: queryArg.scopeNavigation,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ },
+ }),
+ invalidatesTags: ['ScopeNavigation'],
+ }),
+ updateScopeNavigationStatus: build.mutation<
+ UpdateScopeNavigationStatusApiResponse,
+ UpdateScopeNavigationStatusApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/scopenavigations/${queryArg.name}/status`,
+ method: 'PATCH',
+ body: queryArg.patch,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ force: queryArg.force,
+ },
+ }),
+ invalidatesTags: ['ScopeNavigation'],
+ }),
+ listScopeNode: build.query({
+ query: (queryArg) => ({
+ url: `/scopenodes`,
+ params: {
+ pretty: queryArg.pretty,
+ allowWatchBookmarks: queryArg.allowWatchBookmarks,
+ continue: queryArg['continue'],
+ fieldSelector: queryArg.fieldSelector,
+ labelSelector: queryArg.labelSelector,
+ limit: queryArg.limit,
+ resourceVersion: queryArg.resourceVersion,
+ resourceVersionMatch: queryArg.resourceVersionMatch,
+ sendInitialEvents: queryArg.sendInitialEvents,
+ timeoutSeconds: queryArg.timeoutSeconds,
+ watch: queryArg.watch,
+ },
+ }),
+ providesTags: ['ScopeNode'],
+ }),
+ createScopeNode: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopenodes`,
+ method: 'POST',
+ body: queryArg.scopeNode,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ },
+ }),
+ invalidatesTags: ['ScopeNode'],
+ }),
+ deletecollectionScopeNode: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopenodes`,
+ method: 'DELETE',
+ params: {
+ pretty: queryArg.pretty,
+ continue: queryArg['continue'],
+ dryRun: queryArg.dryRun,
+ fieldSelector: queryArg.fieldSelector,
+ gracePeriodSeconds: queryArg.gracePeriodSeconds,
+ ignoreStoreReadErrorWithClusterBreakingPotential: queryArg.ignoreStoreReadErrorWithClusterBreakingPotential,
+ labelSelector: queryArg.labelSelector,
+ limit: queryArg.limit,
+ orphanDependents: queryArg.orphanDependents,
+ propagationPolicy: queryArg.propagationPolicy,
+ resourceVersion: queryArg.resourceVersion,
+ resourceVersionMatch: queryArg.resourceVersionMatch,
+ sendInitialEvents: queryArg.sendInitialEvents,
+ timeoutSeconds: queryArg.timeoutSeconds,
+ },
+ }),
+ invalidatesTags: ['ScopeNode'],
+ }),
+ getScopeNode: build.query({
+ query: (queryArg) => ({
+ url: `/scopenodes/${queryArg.name}`,
+ params: {
+ pretty: queryArg.pretty,
+ },
+ }),
+ providesTags: ['ScopeNode'],
+ }),
+ replaceScopeNode: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopenodes/${queryArg.name}`,
+ method: 'PUT',
+ body: queryArg.scopeNode,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ },
+ }),
+ invalidatesTags: ['ScopeNode'],
+ }),
+ deleteScopeNode: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopenodes/${queryArg.name}`,
+ method: 'DELETE',
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ gracePeriodSeconds: queryArg.gracePeriodSeconds,
+ ignoreStoreReadErrorWithClusterBreakingPotential: queryArg.ignoreStoreReadErrorWithClusterBreakingPotential,
+ orphanDependents: queryArg.orphanDependents,
+ propagationPolicy: queryArg.propagationPolicy,
+ },
+ }),
+ invalidatesTags: ['ScopeNode'],
+ }),
+ updateScopeNode: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopenodes/${queryArg.name}`,
+ method: 'PATCH',
+ body: queryArg.patch,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ force: queryArg.force,
+ },
+ }),
+ invalidatesTags: ['ScopeNode'],
+ }),
+ listScope: build.query({
+ query: (queryArg) => ({
+ url: `/scopes`,
+ params: {
+ pretty: queryArg.pretty,
+ allowWatchBookmarks: queryArg.allowWatchBookmarks,
+ continue: queryArg['continue'],
+ fieldSelector: queryArg.fieldSelector,
+ labelSelector: queryArg.labelSelector,
+ limit: queryArg.limit,
+ resourceVersion: queryArg.resourceVersion,
+ resourceVersionMatch: queryArg.resourceVersionMatch,
+ sendInitialEvents: queryArg.sendInitialEvents,
+ timeoutSeconds: queryArg.timeoutSeconds,
+ watch: queryArg.watch,
+ },
+ }),
+ providesTags: ['Scope'],
+ }),
+ createScope: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopes`,
+ method: 'POST',
+ body: queryArg.scope,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ },
+ }),
+ invalidatesTags: ['Scope'],
+ }),
+ deletecollectionScope: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopes`,
+ method: 'DELETE',
+ params: {
+ pretty: queryArg.pretty,
+ continue: queryArg['continue'],
+ dryRun: queryArg.dryRun,
+ fieldSelector: queryArg.fieldSelector,
+ gracePeriodSeconds: queryArg.gracePeriodSeconds,
+ ignoreStoreReadErrorWithClusterBreakingPotential: queryArg.ignoreStoreReadErrorWithClusterBreakingPotential,
+ labelSelector: queryArg.labelSelector,
+ limit: queryArg.limit,
+ orphanDependents: queryArg.orphanDependents,
+ propagationPolicy: queryArg.propagationPolicy,
+ resourceVersion: queryArg.resourceVersion,
+ resourceVersionMatch: queryArg.resourceVersionMatch,
+ sendInitialEvents: queryArg.sendInitialEvents,
+ timeoutSeconds: queryArg.timeoutSeconds,
+ },
+ }),
+ invalidatesTags: ['Scope'],
+ }),
+ getScope: build.query({
+ query: (queryArg) => ({
+ url: `/scopes/${queryArg.name}`,
+ params: {
+ pretty: queryArg.pretty,
+ },
+ }),
+ providesTags: ['Scope'],
+ }),
+ replaceScope: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopes/${queryArg.name}`,
+ method: 'PUT',
+ body: queryArg.scope,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ },
+ }),
+ invalidatesTags: ['Scope'],
+ }),
+ deleteScope: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopes/${queryArg.name}`,
+ method: 'DELETE',
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ gracePeriodSeconds: queryArg.gracePeriodSeconds,
+ ignoreStoreReadErrorWithClusterBreakingPotential: queryArg.ignoreStoreReadErrorWithClusterBreakingPotential,
+ orphanDependents: queryArg.orphanDependents,
+ propagationPolicy: queryArg.propagationPolicy,
+ },
+ }),
+ invalidatesTags: ['Scope'],
+ }),
+ updateScope: build.mutation({
+ query: (queryArg) => ({
+ url: `/scopes/${queryArg.name}`,
+ method: 'PATCH',
+ body: queryArg.patch,
+ params: {
+ pretty: queryArg.pretty,
+ dryRun: queryArg.dryRun,
+ fieldManager: queryArg.fieldManager,
+ fieldValidation: queryArg.fieldValidation,
+ force: queryArg.force,
+ },
+ }),
+ invalidatesTags: ['Scope'],
+ }),
+ }),
+ overrideExisting: false,
+ });
+export { injectedRtkApi as generatedAPI };
+export type GetApiResourcesApiResponse = /** status 200 OK */ ApiResourceList;
+export type GetApiResourcesApiArg = void;
+export type GetFindScopeDashboardBindingsResultsApiResponse = /** status 200 OK */ FindScopeDashboardBindingsResults;
+export type GetFindScopeDashboardBindingsResultsApiArg = {
+ /** name of the FindScopeDashboardBindingsResults */
+ name: string;
+ /** A scope name (id) to match against, this parameter may be repeated */
+ scope?: string[];
+};
+export type GetFindScopeNavigationsResultsApiResponse = /** status 200 OK */ FindScopeNavigationsResults;
+export type GetFindScopeNavigationsResultsApiArg = {
+ /** name of the FindScopeNavigationsResults */
+ name: string;
+ /** A scope name (id) to match against, this parameter may be repeated */
+ scope?: string[];
+};
+export type GetFindScopeNodeChildrenResultsApiResponse = /** status 200 OK */ FindScopeNodeChildrenResults;
+export type GetFindScopeNodeChildrenResultsApiArg = {
+ /** The parent scope node */
+ parent?: string;
+ query?: string;
+ names?: string[];
+ limit?: number;
+};
+export type ListScopeDashboardBindingApiResponse = /** status 200 OK */ ScopeDashboardBindingList;
+export type ListScopeDashboardBindingApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** allowWatchBookmarks requests watch events with type "BOOKMARK". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. */
+ allowWatchBookmarks?: boolean;
+ /** The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key".
+
+ This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications. */
+ continue?: string;
+ /** A selector to restrict the list of returned objects by their fields. Defaults to everything. */
+ fieldSelector?: string;
+ /** A selector to restrict the list of returned objects by their labels. Defaults to everything. */
+ labelSelector?: string;
+ /** limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.
+
+ The server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned. */
+ limit?: number;
+ /** resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersion?: string;
+ /** resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersionMatch?: string;
+ /** `sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic "Bookmark" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.
+
+ When `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan
+ is interpreted as "data at least as new as the provided `resourceVersion`"
+ and the bookmark event is send when the state is synced
+ to a `resourceVersion` at least as fresh as the one provided by the ListOptions.
+ If `resourceVersion` is unset, this is interpreted as "consistent read" and the
+ bookmark event is send when the state is synced at least to the moment
+ when request started being processed.
+ - `resourceVersionMatch` set to any other value or unset
+ Invalid error is returned.
+
+ Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward compatibility reasons) and to false otherwise. */
+ sendInitialEvents?: boolean;
+ /** Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity. */
+ timeoutSeconds?: number;
+ /** Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion. */
+ watch?: boolean;
+};
+export type CreateScopeDashboardBindingApiResponse = /** status 200 OK */
+ | ScopeDashboardBinding
+ | /** status 201 Created */ ScopeDashboardBinding
+ | /** status 202 Accepted */ ScopeDashboardBinding;
+export type CreateScopeDashboardBindingApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ scopeDashboardBinding: ScopeDashboardBinding;
+};
+export type DeletecollectionScopeDashboardBindingApiResponse = /** status 200 OK */ Status;
+export type DeletecollectionScopeDashboardBindingApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key".
+
+ This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications. */
+ continue?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** A selector to restrict the list of returned objects by their fields. Defaults to everything. */
+ fieldSelector?: string;
+ /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */
+ gracePeriodSeconds?: number;
+ /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */
+ ignoreStoreReadErrorWithClusterBreakingPotential?: boolean;
+ /** A selector to restrict the list of returned objects by their labels. Defaults to everything. */
+ labelSelector?: string;
+ /** limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.
+
+ The server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned. */
+ limit?: number;
+ /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */
+ orphanDependents?: boolean;
+ /** Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground. */
+ propagationPolicy?: string;
+ /** resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersion?: string;
+ /** resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersionMatch?: string;
+ /** `sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic "Bookmark" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.
+
+ When `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan
+ is interpreted as "data at least as new as the provided `resourceVersion`"
+ and the bookmark event is send when the state is synced
+ to a `resourceVersion` at least as fresh as the one provided by the ListOptions.
+ If `resourceVersion` is unset, this is interpreted as "consistent read" and the
+ bookmark event is send when the state is synced at least to the moment
+ when request started being processed.
+ - `resourceVersionMatch` set to any other value or unset
+ Invalid error is returned.
+
+ Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward compatibility reasons) and to false otherwise. */
+ sendInitialEvents?: boolean;
+ /** Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity. */
+ timeoutSeconds?: number;
+};
+export type GetScopeDashboardBindingApiResponse = /** status 200 OK */ ScopeDashboardBinding;
+export type GetScopeDashboardBindingApiArg = {
+ /** name of the ScopeDashboardBinding */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+};
+export type ReplaceScopeDashboardBindingApiResponse = /** status 200 OK */
+ | ScopeDashboardBinding
+ | /** status 201 Created */ ScopeDashboardBinding;
+export type ReplaceScopeDashboardBindingApiArg = {
+ /** name of the ScopeDashboardBinding */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ scopeDashboardBinding: ScopeDashboardBinding;
+};
+export type DeleteScopeDashboardBindingApiResponse = /** status 200 OK */ Status | /** status 202 Accepted */ Status;
+export type DeleteScopeDashboardBindingApiArg = {
+ /** name of the ScopeDashboardBinding */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */
+ gracePeriodSeconds?: number;
+ /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */
+ ignoreStoreReadErrorWithClusterBreakingPotential?: boolean;
+ /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */
+ orphanDependents?: boolean;
+ /** Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground. */
+ propagationPolicy?: string;
+};
+export type UpdateScopeDashboardBindingApiResponse = /** status 200 OK */
+ | ScopeDashboardBinding
+ | /** status 201 Created */ ScopeDashboardBinding;
+export type UpdateScopeDashboardBindingApiArg = {
+ /** name of the ScopeDashboardBinding */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch). */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ /** Force is going to "force" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests. */
+ force?: boolean;
+ patch: Patch;
+};
+export type GetScopeDashboardBindingStatusApiResponse = /** status 200 OK */ ScopeDashboardBinding;
+export type GetScopeDashboardBindingStatusApiArg = {
+ /** name of the ScopeDashboardBinding */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+};
+export type ReplaceScopeDashboardBindingStatusApiResponse = /** status 200 OK */
+ | ScopeDashboardBinding
+ | /** status 201 Created */ ScopeDashboardBinding;
+export type ReplaceScopeDashboardBindingStatusApiArg = {
+ /** name of the ScopeDashboardBinding */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ scopeDashboardBinding: ScopeDashboardBinding;
+};
+export type UpdateScopeDashboardBindingStatusApiResponse = /** status 200 OK */
+ | ScopeDashboardBinding
+ | /** status 201 Created */ ScopeDashboardBinding;
+export type UpdateScopeDashboardBindingStatusApiArg = {
+ /** name of the ScopeDashboardBinding */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch). */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ /** Force is going to "force" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests. */
+ force?: boolean;
+ patch: Patch;
+};
+export type ListScopeNavigationApiResponse = /** status 200 OK */ ScopeNavigationList;
+export type ListScopeNavigationApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** allowWatchBookmarks requests watch events with type "BOOKMARK". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. */
+ allowWatchBookmarks?: boolean;
+ /** The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key".
+
+ This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications. */
+ continue?: string;
+ /** A selector to restrict the list of returned objects by their fields. Defaults to everything. */
+ fieldSelector?: string;
+ /** A selector to restrict the list of returned objects by their labels. Defaults to everything. */
+ labelSelector?: string;
+ /** limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.
+
+ The server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned. */
+ limit?: number;
+ /** resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersion?: string;
+ /** resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersionMatch?: string;
+ /** `sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic "Bookmark" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.
+
+ When `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan
+ is interpreted as "data at least as new as the provided `resourceVersion`"
+ and the bookmark event is send when the state is synced
+ to a `resourceVersion` at least as fresh as the one provided by the ListOptions.
+ If `resourceVersion` is unset, this is interpreted as "consistent read" and the
+ bookmark event is send when the state is synced at least to the moment
+ when request started being processed.
+ - `resourceVersionMatch` set to any other value or unset
+ Invalid error is returned.
+
+ Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward compatibility reasons) and to false otherwise. */
+ sendInitialEvents?: boolean;
+ /** Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity. */
+ timeoutSeconds?: number;
+ /** Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion. */
+ watch?: boolean;
+};
+export type CreateScopeNavigationApiResponse = /** status 200 OK */
+ | ScopeNavigation
+ | /** status 201 Created */ ScopeNavigation
+ | /** status 202 Accepted */ ScopeNavigation;
+export type CreateScopeNavigationApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ scopeNavigation: ScopeNavigation;
+};
+export type DeletecollectionScopeNavigationApiResponse = /** status 200 OK */ Status;
+export type DeletecollectionScopeNavigationApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key".
+
+ This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications. */
+ continue?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** A selector to restrict the list of returned objects by their fields. Defaults to everything. */
+ fieldSelector?: string;
+ /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */
+ gracePeriodSeconds?: number;
+ /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */
+ ignoreStoreReadErrorWithClusterBreakingPotential?: boolean;
+ /** A selector to restrict the list of returned objects by their labels. Defaults to everything. */
+ labelSelector?: string;
+ /** limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.
+
+ The server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned. */
+ limit?: number;
+ /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */
+ orphanDependents?: boolean;
+ /** Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground. */
+ propagationPolicy?: string;
+ /** resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersion?: string;
+ /** resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersionMatch?: string;
+ /** `sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic "Bookmark" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.
+
+ When `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan
+ is interpreted as "data at least as new as the provided `resourceVersion`"
+ and the bookmark event is send when the state is synced
+ to a `resourceVersion` at least as fresh as the one provided by the ListOptions.
+ If `resourceVersion` is unset, this is interpreted as "consistent read" and the
+ bookmark event is send when the state is synced at least to the moment
+ when request started being processed.
+ - `resourceVersionMatch` set to any other value or unset
+ Invalid error is returned.
+
+ Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward compatibility reasons) and to false otherwise. */
+ sendInitialEvents?: boolean;
+ /** Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity. */
+ timeoutSeconds?: number;
+};
+export type GetScopeNavigationApiResponse = /** status 200 OK */ ScopeNavigation;
+export type GetScopeNavigationApiArg = {
+ /** name of the ScopeNavigation */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+};
+export type ReplaceScopeNavigationApiResponse = /** status 200 OK */
+ | ScopeNavigation
+ | /** status 201 Created */ ScopeNavigation;
+export type ReplaceScopeNavigationApiArg = {
+ /** name of the ScopeNavigation */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ scopeNavigation: ScopeNavigation;
+};
+export type DeleteScopeNavigationApiResponse = /** status 200 OK */ Status | /** status 202 Accepted */ Status;
+export type DeleteScopeNavigationApiArg = {
+ /** name of the ScopeNavigation */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */
+ gracePeriodSeconds?: number;
+ /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */
+ ignoreStoreReadErrorWithClusterBreakingPotential?: boolean;
+ /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */
+ orphanDependents?: boolean;
+ /** Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground. */
+ propagationPolicy?: string;
+};
+export type UpdateScopeNavigationApiResponse = /** status 200 OK */
+ | ScopeNavigation
+ | /** status 201 Created */ ScopeNavigation;
+export type UpdateScopeNavigationApiArg = {
+ /** name of the ScopeNavigation */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch). */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ /** Force is going to "force" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests. */
+ force?: boolean;
+ patch: Patch;
+};
+export type GetScopeNavigationStatusApiResponse = /** status 200 OK */ ScopeNavigation;
+export type GetScopeNavigationStatusApiArg = {
+ /** name of the ScopeNavigation */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+};
+export type ReplaceScopeNavigationStatusApiResponse = /** status 200 OK */
+ | ScopeNavigation
+ | /** status 201 Created */ ScopeNavigation;
+export type ReplaceScopeNavigationStatusApiArg = {
+ /** name of the ScopeNavigation */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ scopeNavigation: ScopeNavigation;
+};
+export type UpdateScopeNavigationStatusApiResponse = /** status 200 OK */
+ | ScopeNavigation
+ | /** status 201 Created */ ScopeNavigation;
+export type UpdateScopeNavigationStatusApiArg = {
+ /** name of the ScopeNavigation */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch). */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ /** Force is going to "force" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests. */
+ force?: boolean;
+ patch: Patch;
+};
+export type ListScopeNodeApiResponse = /** status 200 OK */ ScopeNodeList;
+export type ListScopeNodeApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** allowWatchBookmarks requests watch events with type "BOOKMARK". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. */
+ allowWatchBookmarks?: boolean;
+ /** The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key".
+
+ This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications. */
+ continue?: string;
+ /** A selector to restrict the list of returned objects by their fields. Defaults to everything. */
+ fieldSelector?: string;
+ /** A selector to restrict the list of returned objects by their labels. Defaults to everything. */
+ labelSelector?: string;
+ /** limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.
+
+ The server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned. */
+ limit?: number;
+ /** resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersion?: string;
+ /** resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersionMatch?: string;
+ /** `sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic "Bookmark" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.
+
+ When `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan
+ is interpreted as "data at least as new as the provided `resourceVersion`"
+ and the bookmark event is send when the state is synced
+ to a `resourceVersion` at least as fresh as the one provided by the ListOptions.
+ If `resourceVersion` is unset, this is interpreted as "consistent read" and the
+ bookmark event is send when the state is synced at least to the moment
+ when request started being processed.
+ - `resourceVersionMatch` set to any other value or unset
+ Invalid error is returned.
+
+ Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward compatibility reasons) and to false otherwise. */
+ sendInitialEvents?: boolean;
+ /** Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity. */
+ timeoutSeconds?: number;
+ /** Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion. */
+ watch?: boolean;
+};
+export type CreateScopeNodeApiResponse = /** status 200 OK */
+ | ScopeNode
+ | /** status 201 Created */ ScopeNode
+ | /** status 202 Accepted */ ScopeNode;
+export type CreateScopeNodeApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ scopeNode: ScopeNode;
+};
+export type DeletecollectionScopeNodeApiResponse = /** status 200 OK */ Status;
+export type DeletecollectionScopeNodeApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key".
+
+ This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications. */
+ continue?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** A selector to restrict the list of returned objects by their fields. Defaults to everything. */
+ fieldSelector?: string;
+ /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */
+ gracePeriodSeconds?: number;
+ /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */
+ ignoreStoreReadErrorWithClusterBreakingPotential?: boolean;
+ /** A selector to restrict the list of returned objects by their labels. Defaults to everything. */
+ labelSelector?: string;
+ /** limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.
+
+ The server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned. */
+ limit?: number;
+ /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */
+ orphanDependents?: boolean;
+ /** Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground. */
+ propagationPolicy?: string;
+ /** resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersion?: string;
+ /** resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersionMatch?: string;
+ /** `sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic "Bookmark" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.
+
+ When `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan
+ is interpreted as "data at least as new as the provided `resourceVersion`"
+ and the bookmark event is send when the state is synced
+ to a `resourceVersion` at least as fresh as the one provided by the ListOptions.
+ If `resourceVersion` is unset, this is interpreted as "consistent read" and the
+ bookmark event is send when the state is synced at least to the moment
+ when request started being processed.
+ - `resourceVersionMatch` set to any other value or unset
+ Invalid error is returned.
+
+ Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward compatibility reasons) and to false otherwise. */
+ sendInitialEvents?: boolean;
+ /** Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity. */
+ timeoutSeconds?: number;
+};
+export type GetScopeNodeApiResponse = /** status 200 OK */ ScopeNode;
+export type GetScopeNodeApiArg = {
+ /** name of the ScopeNode */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+};
+export type ReplaceScopeNodeApiResponse = /** status 200 OK */ ScopeNode | /** status 201 Created */ ScopeNode;
+export type ReplaceScopeNodeApiArg = {
+ /** name of the ScopeNode */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ scopeNode: ScopeNode;
+};
+export type DeleteScopeNodeApiResponse = /** status 200 OK */ Status | /** status 202 Accepted */ Status;
+export type DeleteScopeNodeApiArg = {
+ /** name of the ScopeNode */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */
+ gracePeriodSeconds?: number;
+ /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */
+ ignoreStoreReadErrorWithClusterBreakingPotential?: boolean;
+ /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */
+ orphanDependents?: boolean;
+ /** Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground. */
+ propagationPolicy?: string;
+};
+export type UpdateScopeNodeApiResponse = /** status 200 OK */ ScopeNode | /** status 201 Created */ ScopeNode;
+export type UpdateScopeNodeApiArg = {
+ /** name of the ScopeNode */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch). */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ /** Force is going to "force" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests. */
+ force?: boolean;
+ patch: Patch;
+};
+export type ListScopeApiResponse = /** status 200 OK */ ScopeList;
+export type ListScopeApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** allowWatchBookmarks requests watch events with type "BOOKMARK". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. */
+ allowWatchBookmarks?: boolean;
+ /** The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key".
+
+ This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications. */
+ continue?: string;
+ /** A selector to restrict the list of returned objects by their fields. Defaults to everything. */
+ fieldSelector?: string;
+ /** A selector to restrict the list of returned objects by their labels. Defaults to everything. */
+ labelSelector?: string;
+ /** limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.
+
+ The server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned. */
+ limit?: number;
+ /** resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersion?: string;
+ /** resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersionMatch?: string;
+ /** `sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic "Bookmark" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.
+
+ When `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan
+ is interpreted as "data at least as new as the provided `resourceVersion`"
+ and the bookmark event is send when the state is synced
+ to a `resourceVersion` at least as fresh as the one provided by the ListOptions.
+ If `resourceVersion` is unset, this is interpreted as "consistent read" and the
+ bookmark event is send when the state is synced at least to the moment
+ when request started being processed.
+ - `resourceVersionMatch` set to any other value or unset
+ Invalid error is returned.
+
+ Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward compatibility reasons) and to false otherwise. */
+ sendInitialEvents?: boolean;
+ /** Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity. */
+ timeoutSeconds?: number;
+ /** Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion. */
+ watch?: boolean;
+};
+export type CreateScopeApiResponse = /** status 200 OK */
+ | Scope
+ | /** status 201 Created */ Scope
+ | /** status 202 Accepted */ Scope;
+export type CreateScopeApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ scope: Scope;
+};
+export type DeletecollectionScopeApiResponse = /** status 200 OK */ Status;
+export type DeletecollectionScopeApiArg = {
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** The continue option should be set when retrieving more results from the server. Since this value is server defined, clients may only use the continue value from a previous query result with identical query parameters (except for the value of continue) and the server may reject a continue value it does not recognize. If the specified continue value is no longer valid whether due to expiration (generally five to fifteen minutes) or a configuration change on the server, the server will respond with a 410 ResourceExpired error together with a continue token. If the client needs a consistent list, it must restart their list without the continue field. Otherwise, the client may send another list request with the token received with the 410 error, the server will respond with a list starting from the next key, but from the latest snapshot, which is inconsistent from the previous list results - objects that are created, modified, or deleted after the first list request will be included in the response, as long as their keys are after the "next key".
+
+ This field is not supported when watch is true. Clients may start a watch from the last resourceVersion value returned by the server and not miss any modifications. */
+ continue?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** A selector to restrict the list of returned objects by their fields. Defaults to everything. */
+ fieldSelector?: string;
+ /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */
+ gracePeriodSeconds?: number;
+ /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */
+ ignoreStoreReadErrorWithClusterBreakingPotential?: boolean;
+ /** A selector to restrict the list of returned objects by their labels. Defaults to everything. */
+ labelSelector?: string;
+ /** limit is a maximum number of responses to return for a list call. If more items exist, the server will set the `continue` field on the list metadata to a value that can be used with the same initial query to retrieve the next set of results. Setting a limit may return fewer than the requested amount of items (up to zero items) in the event all requested objects are filtered out and clients should only use the presence of the continue field to determine whether more results are available. Servers may choose not to support the limit argument and will return all of the available results. If limit is specified and the continue field is empty, clients may assume that no more results are available. This field is not supported if watch is true.
+
+ The server guarantees that the objects returned when using continue will be identical to issuing a single list call without a limit - that is, no objects created, modified, or deleted after the first request is issued will be included in any subsequent continued requests. This is sometimes referred to as a consistent snapshot, and ensures that a client that is using limit to receive smaller chunks of a very large result can ensure they see all possible objects. If objects are updated during a chunked list the version of the object that was present at the time the first list result was calculated is returned. */
+ limit?: number;
+ /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */
+ orphanDependents?: boolean;
+ /** Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground. */
+ propagationPolicy?: string;
+ /** resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersion?: string;
+ /** resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.
+
+ Defaults to unset */
+ resourceVersionMatch?: string;
+ /** `sendInitialEvents=true` may be set together with `watch=true`. In that case, the watch stream will begin with synthetic events to produce the current state of objects in the collection. Once all such events have been sent, a synthetic "Bookmark" event will be sent. The bookmark will report the ResourceVersion (RV) corresponding to the set of objects, and be marked with `"k8s.io/initial-events-end": "true"` annotation. Afterwards, the watch stream will proceed as usual, sending watch events corresponding to changes (subsequent to the RV) to objects watched.
+
+ When `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan
+ is interpreted as "data at least as new as the provided `resourceVersion`"
+ and the bookmark event is send when the state is synced
+ to a `resourceVersion` at least as fresh as the one provided by the ListOptions.
+ If `resourceVersion` is unset, this is interpreted as "consistent read" and the
+ bookmark event is send when the state is synced at least to the moment
+ when request started being processed.
+ - `resourceVersionMatch` set to any other value or unset
+ Invalid error is returned.
+
+ Defaults to true if `resourceVersion=""` or `resourceVersion="0"` (for backward compatibility reasons) and to false otherwise. */
+ sendInitialEvents?: boolean;
+ /** Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity. */
+ timeoutSeconds?: number;
+};
+export type GetScopeApiResponse = /** status 200 OK */ Scope;
+export type GetScopeApiArg = {
+ /** name of the Scope */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+};
+export type ReplaceScopeApiResponse = /** status 200 OK */ Scope | /** status 201 Created */ Scope;
+export type ReplaceScopeApiArg = {
+ /** name of the Scope */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ scope: Scope;
+};
+export type DeleteScopeApiResponse = /** status 200 OK */ Status | /** status 202 Accepted */ Status;
+export type DeleteScopeApiArg = {
+ /** name of the Scope */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately. */
+ gracePeriodSeconds?: number;
+ /** if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it */
+ ignoreStoreReadErrorWithClusterBreakingPotential?: boolean;
+ /** Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the "orphan" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both. */
+ orphanDependents?: boolean;
+ /** Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy. Acceptable values are: 'Orphan' - orphan the dependents; 'Background' - allow the garbage collector to delete the dependents in the background; 'Foreground' - a cascading policy that deletes all dependents in the foreground. */
+ propagationPolicy?: string;
+};
+export type UpdateScopeApiResponse = /** status 200 OK */ Scope | /** status 201 Created */ Scope;
+export type UpdateScopeApiArg = {
+ /** name of the Scope */
+ name: string;
+ /** If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget). */
+ pretty?: string;
+ /** When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed */
+ dryRun?: string;
+ /** fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch). */
+ fieldManager?: string;
+ /** fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered. */
+ fieldValidation?: string;
+ /** Force is going to "force" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests. */
+ force?: boolean;
+ patch: Patch;
+};
+export type ApiResource = {
+ /** categories is a list of the grouped resources this resource belongs to (e.g. 'all') */
+ categories?: string[];
+ /** group is the preferred group of the resource. Empty implies the group of the containing resource list. For subresources, this may have a different value, for example: Scale". */
+ group?: string;
+ /** kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo') */
+ kind: string;
+ /** name is the plural name of the resource. */
+ name: string;
+ /** namespaced indicates if a resource is namespaced or not. */
+ namespaced: boolean;
+ /** shortNames is a list of suggested short names of the resource. */
+ shortNames?: string[];
+ /** singularName is the singular name of the resource. This allows clients to handle plural and singular opaquely. The singularName is more correct for reporting status on a single item and both singular and plural are allowed from the kubectl CLI interface. */
+ singularName: string;
+ /** The hash value of the storage version, the version this resource is converted to when written to the data store. Value must be treated as opaque by clients. Only equality comparison on the value is valid. This is an alpha feature and may change or be removed in the future. The field is populated by the apiserver only if the StorageVersionHash feature gate is enabled. This field will remain optional even if it graduates. */
+ storageVersionHash?: string;
+ /** verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy) */
+ verbs: string[];
+ /** version is the preferred version of the resource. Empty implies the version of the containing resource list For subresources, this may have a different value, for example: v1 (while inside a v1beta1 version of the core resource's group)". */
+ version?: string;
+};
+export type ApiResourceList = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ /** groupVersion is the group and version this APIResourceList is for. */
+ groupVersion: string;
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ /** resources contains the name of the resources and if they are namespaced. */
+ resources: ApiResource[];
+};
+export type Time = string;
+export type FieldsV1 = object;
+export type ManagedFieldsEntry = {
+ /** APIVersion defines the version of this resource that this field set applies to. The format is "group/version" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted. */
+ apiVersion?: string;
+ /** FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: "FieldsV1" */
+ fieldsType?: string;
+ /** FieldsV1 holds the first JSON version format as described in the "FieldsV1" type. */
+ fieldsV1?: FieldsV1;
+ /** Manager is an identifier of the workflow managing these fields. */
+ manager?: string;
+ /** Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'. */
+ operation?: string;
+ /** Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource. */
+ subresource?: string;
+ /** Time is the timestamp of when the ManagedFields entry was added. The timestamp will also be updated if a field is added, the manager changes any of the owned fields value or removes a field. The timestamp does not update when a field is removed from the entry because another manager took it over. */
+ time?: Time;
+};
+export type OwnerReference = {
+ /** API version of the referent. */
+ apiVersion: string;
+ /** If true, AND if the owner has the "foregroundDeletion" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. See https://kubernetes.io/docs/concepts/architecture/garbage-collection/#foreground-deletion for how the garbage collector interacts with this field and enforces the foreground deletion. Defaults to false. To set this field, a user needs "delete" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned. */
+ blockOwnerDeletion?: boolean;
+ /** If true, this reference points to the managing controller. */
+ controller?: boolean;
+ /** Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind: string;
+ /** Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names */
+ name: string;
+ /** UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids */
+ uid: string;
+};
+export type ObjectMeta = {
+ /** Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations */
+ annotations?: {
+ [key: string]: string;
+ };
+ /** CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.
+
+ Populated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata */
+ creationTimestamp?: Time;
+ /** Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only. */
+ deletionGracePeriodSeconds?: number;
+ /** DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field, once the finalizers list is empty. As long as the finalizers list contains items, deletion is blocked. Once the deletionTimestamp is set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.
+
+ Populated by the system when a graceful deletion is requested. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata */
+ deletionTimestamp?: Time;
+ /** Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed. Finalizers may be processed and removed in any order. Order is NOT enforced because it introduces significant risk of stuck finalizers. finalizers is a shared field, any actor with permission can reorder it. If the finalizer list is processed in order, then this can lead to a situation in which the component responsible for the first finalizer in the list is waiting for a signal (field value, external system, or other) produced by a component responsible for a finalizer later in the list, resulting in a deadlock. Without enforced ordering finalizers are free to order amongst themselves and are not vulnerable to ordering changes in the list. */
+ finalizers?: string[];
+ /** GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.
+
+ If this field is specified and the generated name exists, the server will return a 409.
+
+ Applied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency */
+ generateName?: string;
+ /** A sequence number representing a specific generation of the desired state. Populated by the system. Read-only. */
+ generation?: number;
+ /** Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels */
+ labels?: {
+ [key: string]: string;
+ };
+ /** ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like "ci-cd". The set of fields is always in the version that the workflow used when modifying the object. */
+ managedFields?: ManagedFieldsEntry[];
+ /** Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names */
+ name?: string;
+ /** Namespace defines the space within which each name must be unique. An empty namespace is equivalent to the "default" namespace, but "default" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.
+
+ Must be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces */
+ namespace?: string;
+ /** List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller. */
+ ownerReferences?: OwnerReference[];
+ /** An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.
+
+ Populated by the system. Read-only. Value must be treated as opaque by clients and . More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency */
+ resourceVersion?: string;
+ /** Deprecated: selfLink is a legacy read-only field that is no longer populated by the system. */
+ selfLink?: string;
+ /** UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.
+
+ Populated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids */
+ uid?: string;
+};
+export type ScopeDashboardBindingSpec = {
+ dashboard: string;
+ scope: string;
+};
+export type Condition = {
+ /** lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. */
+ lastTransitionTime: Time;
+ /** message is a human readable message indicating details about the transition. This may be an empty string. */
+ message: string;
+ /** observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. */
+ observedGeneration?: number;
+ /** reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. */
+ reason: string;
+ /** status of the condition, one of True, False, Unknown. */
+ status: string;
+ /** type of condition in CamelCase or in foo.example.com/CamelCase. */
+ type: string;
+};
+export type ScopeDashboardBindingStatus = {
+ /** DashboardTitle should be populated and update from the dashboard */
+ dashboardTitle: string;
+ /** DashboardTitleConditions is a list of conditions that are used to determine if the dashboard title is valid. */
+ dashboardTitleConditions?: Condition[];
+ /** Groups is used for the grouping of dashboards that are suggested based on a scope. The source of truth for this information has not been determined yet. */
+ groups?: string[];
+ /** DashboardTitleConditions is a list of conditions that are used to determine if the list of groups is valid. */
+ groupsConditions?: Condition[];
+};
+export type ScopeDashboardBinding = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ metadata?: ObjectMeta;
+ spec?: ScopeDashboardBindingSpec;
+ status?: ScopeDashboardBindingStatus;
+};
+export type FindScopeDashboardBindingsResults = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ items?: ScopeDashboardBinding[];
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ message?: string;
+};
+export type ScopeNavigationSpec = {
+ /** Makes the subscope not selectable, only serving as a way to build the tree. */
+ disableSubScopeSelection?: boolean;
+
+ /** Preload the subscope children, as soon as the ScopeNavigation is loaded. */
+ preLoadSubScopeChildren?: boolean;
+ scope: string;
+ /** Used to navigate to a sub-scope of the main scope. URL will not be used if this is set. */
+ subScope?: string;
+ url: string;
+};
+export type ScopeNavigationStatus = {
+ /** Groups is used for the grouping of dashboards that are suggested based on a scope. The source of truth for this information has not been determined yet. */
+ groups?: string[];
+ /** GroupsConditions is a list of conditions that are used to determine if the list of groups is valid. */
+ groupsConditions?: Condition[];
+ /** Title should be populated and update from the dashboard */
+ title: string;
+ /** TitleConditions is a list of conditions that are used to determine if the title is valid. */
+ titleConditions?: Condition[];
+};
+export type ScopeNavigation = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ metadata?: ObjectMeta;
+ spec?: ScopeNavigationSpec;
+ status?: ScopeNavigationStatus;
+};
+export type FindScopeNavigationsResults = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ items?: ScopeNavigation[];
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ message?: string;
+};
+export type ScopeNodeSpec = {
+ description?: string;
+ disableMultiSelect: boolean;
+ /** scope (later more things) */
+ linkId?: string;
+ /** Possible enum values:
+ - `"scope"` */
+ linkType?: 'scope';
+ nodeType: string;
+ parentName?: string;
+ /** Redirect to a specific path when this node is selected. */
+ redirectPath?: string;
+ /** Displays next to the title to provide more context. */
+ subTitle?: string;
+ title: string;
+};
+export type ScopeNode = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ metadata?: ObjectMeta;
+ spec?: ScopeNodeSpec;
+};
+export type ListMeta = {
+ /** continue may be set if the user set a limit on the number of items returned, and indicates that the server has more data available. The value is opaque and may be used to issue another request to the endpoint that served this list to retrieve the next set of available objects. Continuing a consistent list may not be possible if the server configuration has changed or more than a few minutes have passed. The resourceVersion field returned when using this continue value will be identical to the value in the first response, unless you have received this token from an error message. */
+ continue?: string;
+ /** remainingItemCount is the number of subsequent items in the list which are not included in this list response. If the list request contained label or field selectors, then the number of remaining items is unknown and the field will be left unset and omitted during serialization. If the list is complete (either because it is not chunking or because this is the last chunk), then there are no more remaining items and this field will be left unset and omitted during serialization. Servers older than v1.15 do not set this field. The intended use of the remainingItemCount is *estimating* the size of a collection. Clients should not rely on the remainingItemCount to be set or to be exact. */
+ remainingItemCount?: number;
+ /** String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency */
+ resourceVersion?: string;
+ /** Deprecated: selfLink is a legacy read-only field that is no longer populated by the system. */
+ selfLink?: string;
+};
+export type FindScopeNodeChildrenResults = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ items?: ScopeNode[];
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ metadata?: ListMeta;
+};
+export type ScopeDashboardBindingList = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ items?: ScopeDashboardBinding[];
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ metadata?: ListMeta;
+};
+export type StatusCause = {
+ /** The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.
+
+ Examples:
+ "name" - the field "name" on the current resource
+ "items[0].name" - the field "name" on the first array entry in "items" */
+ field?: string;
+ /** A human-readable description of the cause of the error. This field may be presented as-is to a reader. */
+ message?: string;
+ /** A machine-readable description of the cause of the error. If this value is empty there is no information available. */
+ reason?: string;
+};
+export type StatusDetails = {
+ /** The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes. */
+ causes?: StatusCause[];
+ /** The group attribute of the resource associated with the status StatusReason. */
+ group?: string;
+ /** The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ /** The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described). */
+ name?: string;
+ /** If specified, the time in seconds before the operation should be retried. Some errors may indicate the client must take an alternate action - for those errors this field may indicate how long to wait before taking the alternate action. */
+ retryAfterSeconds?: number;
+ /** UID of the resource. (when there is a single resource which can be described). More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids */
+ uid?: string;
+};
+export type Status = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ /** Suggested HTTP return code for this status, 0 if not set. */
+ code?: number;
+ /** Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type. */
+ details?: StatusDetails;
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ /** A human-readable description of the status of this operation. */
+ message?: string;
+ /** Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ metadata?: ListMeta;
+ /** A machine-readable description of why this operation is in the "Failure" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it. */
+ reason?: string;
+ /** Status of the operation. One of: "Success" or "Failure". More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status */
+ status?: string;
+};
+export type Patch = object;
+export type ScopeNavigationList = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ items?: ScopeNavigation[];
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ metadata?: ListMeta;
+};
+export type ScopeNodeList = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ items?: ScopeNode[];
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ metadata?: ListMeta;
+};
+export type ScopeFilter = {
+ key: string;
+ /** Possible enum values:
+ - `"equals"`
+ - `"not-equals"`
+ - `"not-one-of"`
+ - `"one-of"`
+ - `"regex-match"`
+ - `"regex-not-match"` */
+ operator: 'equals' | 'not-equals' | 'not-one-of' | 'one-of' | 'regex-match' | 'regex-not-match';
+ value: string;
+ /** Values is used for operators that require multiple values (e.g. one-of and not-one-of). */
+ values?: string[];
+};
+export type ScopeSpec = {
+ /** Provides a default path for the scope. This refers to a list of nodes in the selector. This is used to display the title next to the selected scope and expand the selector to the proper path. This will override whichever is selected from in the selector. The path is a list of node ids, starting at the direct parent of the selected node towards the root. */
+ defaultPath?: string[];
+ filters?: ScopeFilter[];
+ title: string;
+};
+export type Scope = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ metadata?: ObjectMeta;
+ spec?: ScopeSpec;
+};
+export type ScopeList = {
+ /** APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources */
+ apiVersion?: string;
+ items?: Scope[];
+ /** Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds */
+ kind?: string;
+ metadata?: ListMeta;
+};
diff --git a/public/app/api/clients/scope/v0alpha1/index.ts b/public/app/api/clients/scope/v0alpha1/index.ts
new file mode 100644
index 00000000000..9b1365f667e
--- /dev/null
+++ b/public/app/api/clients/scope/v0alpha1/index.ts
@@ -0,0 +1,3 @@
+import { generatedAPI } from './endpoints.gen';
+
+export const scopeAPIv0alpha1 = generatedAPI;
diff --git a/public/app/api/clients/scope/v0alpha1/sync-from-enterprise.sh b/public/app/api/clients/scope/v0alpha1/sync-from-enterprise.sh
new file mode 100755
index 00000000000..2e5cfa2c762
--- /dev/null
+++ b/public/app/api/clients/scope/v0alpha1/sync-from-enterprise.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+# Syncs the scope API client from Enterprise to OSS.
+#
+# This script:
+# 1. Regenerates the Enterprise API client from the OpenAPI spec
+# 2. Copies the generated endpoints.gen.ts to OSS
+#
+# Prerequisites:
+# - The OpenAPI spec must exist at data/openapi/scope.grafana.app-v0alpha1.json
+# (generated by running TestIntegrationOpenAPIs in pkg/extensions/apiserver/tests/)
+#
+# Usage: ./sync-from-enterprise.sh
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+GRAFANA_ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)"
+
+# Source and destination directories for the generated API client
+ENTERPRISE_SCOPE_API_DIR="$GRAFANA_ROOT/public/app/extensions/api/clients/scope/v0alpha1"
+OSS_SCOPE_API_DIR="$SCRIPT_DIR"
+
+cd "$GRAFANA_ROOT"
+
+# Check if OpenAPI spec exists
+if [ ! -f "data/openapi/scope.grafana.app-v0alpha1.json" ]; then
+ echo "Error: OpenAPI spec not found at data/openapi/scope.grafana.app-v0alpha1.json"
+ echo "Run TestIntegrationOpenAPIs in pkg/extensions/apiserver/tests/ to generate it."
+ exit 1
+fi
+
+echo "Step 1: Generating Enterprise API client from OpenAPI spec..."
+yarn workspace @grafana/api-clients process-specs && npx rtk-query-codegen-openapi ./local/generate-enterprise-apis.ts
+
+if [ ! -f "$ENTERPRISE_SCOPE_API_DIR/endpoints.gen.ts" ]; then
+ echo "Error: Enterprise endpoints.gen.ts not found after generation"
+ exit 1
+fi
+
+echo "Step 2: Copying endpoints.gen.ts from Enterprise to OSS..."
+cp "$ENTERPRISE_SCOPE_API_DIR/endpoints.gen.ts" "$OSS_SCOPE_API_DIR/endpoints.gen.ts"
+
+echo "Done! Scope API client synced from Enterprise."
diff --git a/public/app/core/reducers/root.ts b/public/app/core/reducers/root.ts
index d22835ac69e..5c817bf74cd 100644
--- a/public/app/core/reducers/root.ts
+++ b/public/app/core/reducers/root.ts
@@ -3,6 +3,7 @@ import { AnyAction, combineReducers } from 'redux';
import { allReducers as allApiClientReducers } from '@grafana/api-clients/rtkq';
import { generatedAPI as legacyAPI } from '@grafana/api-clients/rtkq/legacy';
+import { scopeAPIv0alpha1 } from 'app/api/clients/scope/v0alpha1';
import sharedReducers from 'app/core/reducers';
import ldapReducers from 'app/features/admin/state/reducers';
import alertingReducers from 'app/features/alerting/state/reducers';
@@ -52,6 +53,7 @@ const rootReducers = {
[alertingApi.reducerPath]: alertingApi.reducer,
[publicDashboardApi.reducerPath]: publicDashboardApi.reducer,
[browseDashboardsAPI.reducerPath]: browseDashboardsAPI.reducer,
+ [scopeAPIv0alpha1.reducerPath]: scopeAPIv0alpha1.reducer,
...allApiClientReducers,
};
diff --git a/public/app/features/alerting/unified/api/alertmanagerApi.ts b/public/app/features/alerting/unified/api/alertmanagerApi.ts
index 6f9a99194cc..b0c41c84acd 100644
--- a/public/app/features/alerting/unified/api/alertmanagerApi.ts
+++ b/public/app/features/alerting/unified/api/alertmanagerApi.ts
@@ -108,7 +108,9 @@ export const alertmanagerApi = alertingApi.injectEndpoints({
}),
grafanaNotifiers: build.query({
- query: () => ({ url: '/api/alert-notifiers' }),
+ // NOTE: version=2 parameter required for versioned schema (PR #109969)
+ // This parameter will be removed in future when v2 becomes default
+ query: () => ({ url: '/api/alert-notifiers?version=2' }),
transformResponse: (response: NotifierDTO[]) => {
const populateSecureFieldKey = (
option: NotificationChannelOption,
@@ -121,11 +123,16 @@ export const alertmanagerApi = alertingApi.injectEndpoints({
),
});
+ // Keep versions array intact for version-specific options lookup
+ // Transform options with secureFieldKey population
return response.map((notifier) => ({
...notifier,
- options: notifier.options.map((option) => {
- return populateSecureFieldKey(option, '');
- }),
+ options: (notifier.options || []).map((option) => populateSecureFieldKey(option, '')),
+ // Also transform options within each version
+ versions: notifier.versions?.map((version) => ({
+ ...version,
+ options: (version.options || []).map((option) => populateSecureFieldKey(option, '')),
+ })),
}));
},
}),
diff --git a/public/app/features/alerting/unified/api/prometheusApi.ts b/public/app/features/alerting/unified/api/prometheusApi.ts
index 9432da368b6..c03b52eab4e 100644
--- a/public/app/features/alerting/unified/api/prometheusApi.ts
+++ b/public/app/features/alerting/unified/api/prometheusApi.ts
@@ -46,6 +46,7 @@ export type GrafanaPromRulesOptions = Omit {
+ describe('when the provenance is file', () => {
+ it('should render the badge with the correct text', () => {
+ render();
+
+ expect(screen.getByText('Provisioned')).toBeInTheDocument();
+ expect(screen.queryByText('Imported')).not.toBeInTheDocument();
+ });
+
+ it('should render correct tooltip text', async () => {
+ const { user } = render();
+
+ const badge = screen.getByText('Provisioned');
+ await user.hover(badge);
+
+ expect(
+ screen.getByText('This resource has been provisioned via file and cannot be edited through the UI')
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('when the provenance is ConvertedPrometheus', () => {
+ it('should render the badge with the correct text', () => {
+ render();
+
+ expect(screen.getByText('Imported')).toBeInTheDocument();
+ expect(screen.queryByText('Provisioned')).not.toBeInTheDocument();
+ });
+
+ it('should render correct tooltip text', async () => {
+ const { user } = render();
+
+ const badge = screen.getByText('Imported');
+ await user.hover(badge);
+
+ expect(
+ screen.getByText('This resource has been provisioned via Prometheus/Mimir and cannot be edited through the UI')
+ ).toBeInTheDocument();
+ });
+ });
+
+ describe('when the provenance is API', () => {
+ it('should render the badge with the correct text', () => {
+ render();
+
+ expect(screen.getByText('Provisioned')).toBeInTheDocument();
+ expect(screen.queryByText('Imported')).not.toBeInTheDocument();
+ });
+
+ it('should render correct tooltip text', async () => {
+ const { user } = render();
+
+ const badge = screen.getByText('Provisioned');
+ await user.hover(badge);
+
+ expect(
+ screen.getByText('This resource has been provisioned via api and cannot be edited through the UI')
+ ).toBeInTheDocument();
+ });
+ });
+});
diff --git a/public/app/features/alerting/unified/components/Provisioning.tsx b/public/app/features/alerting/unified/components/Provisioning.tsx
index 73beb8a0865..5a9deb8a6bd 100644
--- a/public/app/features/alerting/unified/components/Provisioning.tsx
+++ b/public/app/features/alerting/unified/components/Provisioning.tsx
@@ -3,6 +3,8 @@ import { ComponentPropsWithoutRef } from 'react';
import { Trans, t } from '@grafana/i18n';
import { Alert, Badge, Tooltip } from '@grafana/ui';
+import { KnownProvenance } from '../types/knownProvenance';
+
export enum ProvisionedResource {
ContactPoint = 'contact point',
Template = 'template',
@@ -36,6 +38,24 @@ export const ProvisioningAlert = ({ resource, ...rest }: ProvisioningAlertProps)
);
};
+export const ImportedContactPointAlert = (props: ExtraAlertProps) => {
+ return (
+
+
+ This contact point contains integrations that were imported from an external Alertmanager and is currently
+ read-only. The integrations will become editable after the migration process is complete.
+
+
+ );
+};
+
export const ProvisioningBadge = ({
tooltip,
provenance,
@@ -46,11 +66,17 @@ export const ProvisioningBadge = ({
*/
provenance?: string;
}) => {
- const badge = ;
+ const isConvertedPrometheus = provenance === KnownProvenance.ConvertedPrometheus;
+ const badgeText = isConvertedPrometheus
+ ? t('alerting.provisioning-badge.badge.text-converted-prometheus', 'Imported')
+ : t('alerting.provisioning-badge.badge.text-provisioned', 'Provisioned');
+ const badgeColor = isConvertedPrometheus ? 'blue' : 'purple';
+ const badge = ;
if (tooltip) {
+ const provenanceText = isConvertedPrometheus ? 'Prometheus/Mimir' : provenance;
const provenanceTooltip = (
-
+
This resource has been provisioned via {{ provenance }} and cannot be edited through the UI
);
diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPointHeader.test.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPointHeader.test.tsx
new file mode 100644
index 00000000000..2879bbb57e1
--- /dev/null
+++ b/public/app/features/alerting/unified/components/contact-points/ContactPointHeader.test.tsx
@@ -0,0 +1,60 @@
+import { render, screen } from 'test/test-utils';
+
+import { AccessControlAction } from 'app/types/accessControl';
+
+import { setupMswServer } from '../../mockApi';
+import { grantUserPermissions } from '../../mocks';
+import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
+import { KnownProvenance } from '../../types/knownProvenance';
+
+import { ContactPointHeader } from './ContactPointHeader';
+import { ContactPointWithMetadata } from './utils';
+
+setupMswServer();
+
+const renderWithProvider = (component: React.ReactElement, alertmanagerSourceName?: string) => {
+ return render(
+
+ {component}
+
+ );
+};
+
+describe('ContactPointHeader', () => {
+ beforeEach(() => {
+ grantUserPermissions([
+ AccessControlAction.AlertingNotificationsRead,
+ AccessControlAction.AlertingNotificationsWrite,
+ ]);
+ });
+
+ const mockContactPoint: ContactPointWithMetadata = {
+ id: 'test-contact-point',
+ name: 'Test Contact Point',
+ provenance: KnownProvenance.API,
+ policies: [],
+ grafana_managed_receiver_configs: [],
+ };
+
+ it('shows Provisioned badge when contact point has file provenance via K8s annotations', () => {
+ const contactPointWithFile = {
+ ...mockContactPoint,
+ provenance: KnownProvenance.File,
+ };
+
+ renderWithProvider();
+
+ expect(screen.getByText('Provisioned')).toBeInTheDocument();
+ });
+
+ it('shows correct badge when contact point has converted_prometheus provenance', () => {
+ const contactPointWithConvertedPrometheus = {
+ ...mockContactPoint,
+ provenance: KnownProvenance.ConvertedPrometheus,
+ };
+
+ renderWithProvider();
+
+ expect(screen.getByText('Imported')).toBeInTheDocument();
+ });
+});
diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPointHeader.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPointHeader.tsx
index 0fb1403cb35..6e45c1b6512 100644
--- a/public/app/features/alerting/unified/components/contact-points/ContactPointHeader.tsx
+++ b/public/app/features/alerting/unified/components/contact-points/ContactPointHeader.tsx
@@ -13,6 +13,7 @@ import {
canDeleteEntity,
canEditEntity,
getAnnotation,
+ isProvisionedResource,
shouldUseK8sApi,
} from 'app/features/alerting/unified/utils/k8s/utils';
@@ -31,13 +32,15 @@ interface ContactPointHeaderProps {
}
export const ContactPointHeader = ({ contactPoint, onDelete }: ContactPointHeaderProps) => {
- const { name, id, provisioned, policies = [] } = contactPoint;
+ const { name, id, provenance, policies = [] } = contactPoint;
const styles = useStyles2(getStyles);
const [showPermissionsDrawer, setShowPermissionsDrawer] = useState(false);
const { selectedAlertmanager } = useAlertmanager();
const usingK8sApi = shouldUseK8sApi(selectedAlertmanager!);
+ const isProvisioned = isProvisionedResource(provenance);
+
const [exportSupported, exportAllowed] = useAlertmanagerAbility(AlertmanagerAction.ExportContactPoint);
const [editSupported, editAllowed] = useAlertmanagerAbility(AlertmanagerAction.UpdateContactPoint);
const [deleteSupported, deleteAllowed] = useAlertmanagerAbility(AlertmanagerAction.UpdateContactPoint);
@@ -70,14 +73,14 @@ export const ContactPointHeader = ({ contactPoint, onDelete }: ContactPointHeade
/** Does the current user have permissions to edit the contact point? */
const hasAbilityToEdit = usingK8sApi ? canEditEntity(contactPoint) : editAllowed;
/** Can the contact point actually be edited via the UI? */
- const contactPointIsEditable = !provisioned;
+ const contactPointIsEditable = !isProvisioned;
/** Given the alertmanager, the user's permissions, and the state of the contact point - can it actually be edited? */
const canEdit = editSupported && hasAbilityToEdit && contactPointIsEditable;
/** Does the current user have permissions to delete the contact point? */
const hasAbilityToDelete = usingK8sApi ? canDeleteEntity(contactPoint) : deleteAllowed;
/** Can the contact point actually be deleted, regardless of permissions? i.e. ensuring it isn't provisioned and isn't referenced elsewhere */
- const contactPointIsDeleteable = !provisioned && !numberOfPoliciesPreventingDeletion && !numberOfRules;
+ const contactPointIsDeleteable = !isProvisioned && !numberOfPoliciesPreventingDeletion && !numberOfRules;
/** Given the alertmanager, the user's permissions, and the state of the contact point - can it actually be deleted? */
const canBeDeleted = deleteSupported && hasAbilityToDelete && contactPointIsDeleteable;
@@ -130,7 +133,7 @@ export const ContactPointHeader = ({ contactPoint, onDelete }: ContactPointHeade
const reasonsDeleteIsDisabled = [
!hasAbilityToDelete ? cannotDeleteNoPermissions : '',
- provisioned ? cannotDeleteProvisioned : '',
+ isProvisioned ? cannotDeleteProvisioned : '',
numberOfPoliciesPreventingDeletion > 0 ? cannotDeletePolicies : '',
numberOfRules ? cannotDeleteRules : '',
].filter(Boolean);
@@ -209,15 +212,13 @@ export const ContactPointHeader = ({ contactPoint, onDelete }: ContactPointHeade
{referencedByRulesText}
)}
- {provisioned && (
-
- )}
+ {isProvisioned && }
{!isReferencedByAnything && }
{
});
it('should disable buttons when provisioned', async () => {
- const { user } = renderWithProvider();
+ const { user } = renderWithProvider(
+
+ );
expect(screen.getByText(/provisioned/i)).toBeInTheDocument();
diff --git a/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap b/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap
index 18a5bae9e28..7524d3ba37a 100644
--- a/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap
+++ b/public/app/features/alerting/unified/components/contact-points/__snapshots__/useContactPoints.test.tsx.snap
@@ -50,7 +50,7 @@ exports[`useContactPoints should return contact points with status 1`] = `
},
},
],
- "provisioned": false,
+ "provenance": undefined,
},
{
"grafana_managed_receiver_configs": [
@@ -93,7 +93,7 @@ exports[`useContactPoints should return contact points with status 1`] = `
},
"name": "lotsa-emails",
"policies": [],
- "provisioned": false,
+ "provenance": undefined,
},
{
"grafana_managed_receiver_configs": [
@@ -129,7 +129,7 @@ exports[`useContactPoints should return contact points with status 1`] = `
},
"name": "OnCall Conctact point",
"policies": [],
- "provisioned": false,
+ "provenance": undefined,
},
{
"grafana_managed_receiver_configs": [
@@ -178,7 +178,7 @@ exports[`useContactPoints should return contact points with status 1`] = `
},
},
],
- "provisioned": true,
+ "provenance": "api",
},
{
"grafana_managed_receiver_configs": [
@@ -243,7 +243,7 @@ exports[`useContactPoints should return contact points with status 1`] = `
},
"name": "Slack with multiple channels",
"policies": [],
- "provisioned": false,
+ "provenance": undefined,
},
],
"error": undefined,
@@ -301,7 +301,7 @@ exports[`useContactPoints when having oncall plugin installed and no alert manag
},
},
],
- "provisioned": false,
+ "provenance": undefined,
},
{
"grafana_managed_receiver_configs": [
@@ -344,7 +344,7 @@ exports[`useContactPoints when having oncall plugin installed and no alert manag
},
"name": "lotsa-emails",
"policies": [],
- "provisioned": false,
+ "provenance": undefined,
},
{
"grafana_managed_receiver_configs": [
@@ -383,7 +383,7 @@ exports[`useContactPoints when having oncall plugin installed and no alert manag
},
"name": "OnCall Conctact point",
"policies": [],
- "provisioned": false,
+ "provenance": undefined,
},
{
"grafana_managed_receiver_configs": [
@@ -432,7 +432,7 @@ exports[`useContactPoints when having oncall plugin installed and no alert manag
},
},
],
- "provisioned": true,
+ "provenance": "api",
},
{
"grafana_managed_receiver_configs": [
@@ -497,7 +497,7 @@ exports[`useContactPoints when having oncall plugin installed and no alert manag
},
"name": "Slack with multiple channels",
"policies": [],
- "provisioned": false,
+ "provenance": undefined,
},
],
"error": undefined,
diff --git a/public/app/features/alerting/unified/components/contact-points/useContactPoints.test.tsx b/public/app/features/alerting/unified/components/contact-points/useContactPoints.test.tsx
index 2ac9c04f981..b539a239bd6 100644
--- a/public/app/features/alerting/unified/components/contact-points/useContactPoints.test.tsx
+++ b/public/app/features/alerting/unified/components/contact-points/useContactPoints.test.tsx
@@ -6,10 +6,13 @@ import { disablePlugin } from 'app/features/alerting/unified/mocks/server/config
import { setOnCallIntegrations } from 'app/features/alerting/unified/mocks/server/handlers/plugins/configure-plugins';
import { SupportedPlugin } from 'app/features/alerting/unified/types/pluginBridges';
import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
+import { AlertManagerCortexConfig } from 'app/plugins/datasource/alertmanager/types';
import { AccessControlAction } from 'app/types/accessControl';
import { setupMswServer } from '../../mockApi';
import { grantUserPermissions } from '../../mocks';
+import { setAlertmanagerConfig } from '../../mocks/server/entities/alertmanagers';
+import { KnownProvenance } from '../../types/knownProvenance';
import { useContactPointsWithStatus } from './useContactPoints';
@@ -69,4 +72,235 @@ describe('useContactPoints', () => {
expect(snapshot).toMatchSnapshot();
});
});
+
+ describe('Provenance handling', () => {
+ it('should extract provenance when provenance is "api"', async () => {
+ // Set up alertmanager config with a receiver that has API provenance
+ const config: AlertManagerCortexConfig = {
+ template_files: {},
+ alertmanager_config: {
+ receivers: [
+ {
+ name: 'api-provenance-contact-point',
+ grafana_managed_receiver_configs: [
+ {
+ uid: 'test-uid-1',
+ name: 'api-provenance-contact-point',
+ type: 'email',
+ disableResolveMessage: false,
+ settings: {
+ addresses: 'test@example.com',
+ },
+ secureFields: {},
+ provenance: 'api', // This will be used by the K8s mock handler
+ },
+ ],
+ },
+ ],
+ },
+ };
+ setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, config);
+
+ const { result } = renderHook(
+ () =>
+ useContactPointsWithStatus({
+ alertmanager: GRAFANA_RULES_SOURCE_NAME,
+ fetchPolicies: false,
+ fetchStatuses: false,
+ }),
+ { wrapper }
+ );
+
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(false);
+ });
+
+ const contactPoint = result.current.contactPoints?.find((cp) => cp.name === 'api-provenance-contact-point');
+ expect(contactPoint).toBeDefined();
+ expect(contactPoint?.provenance).toBe(KnownProvenance.API);
+ });
+
+ it('should extract provenance when provenance is "file"', async () => {
+ const config: AlertManagerCortexConfig = {
+ template_files: {},
+ alertmanager_config: {
+ receivers: [
+ {
+ name: 'file-provenance-contact-point',
+ grafana_managed_receiver_configs: [
+ {
+ uid: 'test-uid-2',
+ name: 'file-provenance-contact-point',
+ type: 'email',
+ disableResolveMessage: false,
+ settings: {
+ addresses: 'test@example.com',
+ },
+ secureFields: {},
+ provenance: 'file',
+ },
+ ],
+ },
+ ],
+ },
+ };
+ setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, config);
+
+ const { result } = renderHook(
+ () =>
+ useContactPointsWithStatus({
+ alertmanager: GRAFANA_RULES_SOURCE_NAME,
+ fetchPolicies: false,
+ fetchStatuses: false,
+ }),
+ { wrapper }
+ );
+
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(false);
+ });
+
+ const contactPoint = result.current.contactPoints?.find((cp) => cp.name === 'file-provenance-contact-point');
+ expect(contactPoint).toBeDefined();
+ expect(contactPoint?.provenance).toBe(KnownProvenance.File);
+ });
+
+ it('should extract provenance when provenance is "converted_prometheus"', async () => {
+ const config: AlertManagerCortexConfig = {
+ template_files: {},
+ alertmanager_config: {
+ receivers: [
+ {
+ name: 'mimir-provenance-contact-point',
+ grafana_managed_receiver_configs: [
+ {
+ uid: 'test-uid-3',
+ name: 'mimir-provenance-contact-point',
+ type: 'email',
+ disableResolveMessage: false,
+ settings: {
+ addresses: 'test@example.com',
+ },
+ secureFields: {},
+ provenance: 'converted_prometheus',
+ },
+ ],
+ },
+ ],
+ },
+ };
+ setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, config);
+
+ const { result } = renderHook(
+ () =>
+ useContactPointsWithStatus({
+ alertmanager: GRAFANA_RULES_SOURCE_NAME,
+ fetchPolicies: false,
+ fetchStatuses: false,
+ }),
+ { wrapper }
+ );
+
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(false);
+ });
+
+ const contactPoint = result.current.contactPoints?.find((cp) => cp.name === 'mimir-provenance-contact-point');
+ expect(contactPoint).toBeDefined();
+ expect(contactPoint?.provenance).toBe(KnownProvenance.ConvertedPrometheus);
+ });
+
+ it('should map "none" provenance annotation to undefined', async () => {
+ const config: AlertManagerCortexConfig = {
+ template_files: {},
+ alertmanager_config: {
+ receivers: [
+ {
+ name: 'none-provenance-contact-point',
+ grafana_managed_receiver_configs: [
+ {
+ uid: 'test-uid-4',
+ name: 'none-provenance-contact-point',
+ type: 'email',
+ disableResolveMessage: false,
+ settings: {
+ addresses: 'test@example.com',
+ },
+ secureFields: {},
+ // No provenance field - will default to PROVENANCE_NONE in mock handler
+ },
+ ],
+ },
+ ],
+ },
+ };
+ setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, config);
+
+ const { result } = renderHook(
+ () =>
+ useContactPointsWithStatus({
+ alertmanager: GRAFANA_RULES_SOURCE_NAME,
+ fetchPolicies: false,
+ fetchStatuses: false,
+ }),
+ { wrapper }
+ );
+
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(false);
+ });
+
+ const contactPoint = result.current.contactPoints?.find((cp) => cp.name === 'none-provenance-contact-point');
+ expect(contactPoint).toBeDefined();
+ // The mock handler sets PROVENANCE_NONE ('none') when no provenance is found
+ // parseK8sReceiver converts 'none' to undefined
+ expect(contactPoint?.provenance).toBeUndefined();
+ });
+
+ it('should handle missing annotations gracefully', async () => {
+ // This test verifies that when annotations are undefined, provenance is handled correctly
+ const config: AlertManagerCortexConfig = {
+ template_files: {},
+ alertmanager_config: {
+ receivers: [
+ {
+ name: 'no-annotations-contact-point',
+ grafana_managed_receiver_configs: [
+ {
+ uid: 'test-uid-5',
+ name: 'no-annotations-contact-point',
+ type: 'email',
+ disableResolveMessage: false,
+ settings: {
+ addresses: 'test@example.com',
+ },
+ secureFields: {},
+ },
+ ],
+ },
+ ],
+ },
+ };
+ setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, config);
+
+ const { result } = renderHook(
+ () =>
+ useContactPointsWithStatus({
+ alertmanager: GRAFANA_RULES_SOURCE_NAME,
+ fetchPolicies: false,
+ fetchStatuses: false,
+ }),
+ { wrapper }
+ );
+
+ await waitFor(() => {
+ expect(result.current.isLoading).toBe(false);
+ });
+
+ const contactPoint = result.current.contactPoints?.find((cp) => cp.name === 'no-annotations-contact-point');
+ expect(contactPoint).toBeDefined();
+ // When annotations are missing, the mock handler should set provenance to undefined
+ expect(contactPoint?.provenance).toBeUndefined();
+ });
+ });
});
diff --git a/public/app/features/alerting/unified/components/contact-points/useContactPoints.ts b/public/app/features/alerting/unified/components/contact-points/useContactPoints.ts
index 6627d5f69d2..bf5e8e5fcdf 100644
--- a/public/app/features/alerting/unified/components/contact-points/useContactPoints.ts
+++ b/public/app/features/alerting/unified/components/contact-points/useContactPoints.ts
@@ -11,7 +11,7 @@ import { ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Receiver } f
import { BaseAlertmanagerArgs, Skippable } from 'app/features/alerting/unified/types/hooks';
import { cloudNotifierTypes } from 'app/features/alerting/unified/utils/cloud-alertmanager-notifier-types';
import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
-import { isK8sEntityProvisioned, shouldUseK8sApi } from 'app/features/alerting/unified/utils/k8s/utils';
+import { shouldUseK8sApi } from 'app/features/alerting/unified/utils/k8s/utils';
import { GrafanaManagedContactPoint, Receiver } from 'app/plugins/datasource/alertmanager/types';
import { getAPINamespace } from '../../../../../api/utils';
@@ -21,7 +21,9 @@ import { useAsync } from '../../hooks/useAsync';
import { usePluginBridge } from '../../hooks/usePluginBridge';
import { useProduceNewAlertmanagerConfiguration } from '../../hooks/useProduceNewAlertmanagerConfig';
import { addReceiverAction, deleteReceiverAction, updateReceiverAction } from '../../reducers/alertmanager/receivers';
+import { KnownProvenance } from '../../types/knownProvenance';
import { getIrmIfPresentOrOnCallPluginId } from '../../utils/config';
+import { K8sAnnotations } from '../../utils/k8s/constants';
import { enhanceContactPointsWithMetadata } from './utils';
@@ -78,10 +80,13 @@ const useOnCallIntegrations = ({ skip }: Skippable = {}) => {
type K8sReceiver = ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Receiver;
const parseK8sReceiver = (item: K8sReceiver): GrafanaManagedContactPoint => {
+ const metadataProvenance = item.metadata.annotations?.[K8sAnnotations.Provenance];
+ const provenance = metadataProvenance === KnownProvenance.None ? undefined : metadataProvenance;
+
return {
id: item.metadata.name || item.metadata.uid || item.spec.title,
name: item.spec.title,
- provisioned: isK8sEntityProvisioned(item),
+ provenance: provenance,
grafana_managed_receiver_configs: item.spec.integrations,
metadata: item.metadata,
};
diff --git a/public/app/features/alerting/unified/components/contact-points/useNotificationTemplates.ts b/public/app/features/alerting/unified/components/contact-points/useNotificationTemplates.ts
index 91739aeca61..3083b66d300 100644
--- a/public/app/features/alerting/unified/components/contact-points/useNotificationTemplates.ts
+++ b/public/app/features/alerting/unified/components/contact-points/useNotificationTemplates.ts
@@ -16,7 +16,8 @@ import {
deleteNotificationTemplateAction,
updateNotificationTemplateAction,
} from '../../reducers/alertmanager/notificationTemplates';
-import { K8sAnnotations, PROVENANCE_NONE } from '../../utils/k8s/constants';
+import { KnownProvenance } from '../../types/knownProvenance';
+import { K8sAnnotations } from '../../utils/k8s/constants';
import { getAnnotation, shouldUseK8sApi } from '../../utils/k8s/utils';
import { ensureDefine } from '../../utils/templates';
import { TemplateFormValues } from '../receivers/TemplateForm';
@@ -79,7 +80,7 @@ function templateGroupsToTemplates(
function templateGroupToTemplate(
templateGroup: ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1TemplateGroup
): NotificationTemplate {
- const provenance = getAnnotation(templateGroup, K8sAnnotations.Provenance) ?? PROVENANCE_NONE;
+ const provenance = getAnnotation(templateGroup, K8sAnnotations.Provenance) ?? KnownProvenance.None;
return {
// K8s entities should always have a metadata.name property. The type is marked as optional because it's also used in other places
uid: templateGroup.metadata.name ?? templateGroup.spec.title,
@@ -96,8 +97,8 @@ function amConfigToTemplates(config: AlertManagerCortexConfig): NotificationTemp
uid: title,
title,
content,
- // Undefined, null or empty string should be converted to PROVENANCE_NONE
- provenance: (config.template_file_provenances ?? {})[title] || PROVENANCE_NONE,
+ // Undefined, null or empty string should be converted to KnownProvenance.None
+ provenance: (config.template_file_provenances ?? {})[title] || KnownProvenance.None,
missing: !templates.includes(title),
}));
}
@@ -272,7 +273,7 @@ export function useValidateNotificationTemplate({
}
interface NotificationTemplateMetadata {
- isProvisioned: boolean;
+ provenance?: string;
}
export function useNotificationTemplateMetadata(
@@ -280,11 +281,11 @@ export function useNotificationTemplateMetadata(
): NotificationTemplateMetadata {
if (!template) {
return {
- isProvisioned: false,
+ provenance: KnownProvenance.None,
};
}
return {
- isProvisioned: Boolean(template.provenance) && template.provenance !== PROVENANCE_NONE,
+ provenance: template.provenance,
};
}
diff --git a/public/app/features/alerting/unified/components/contact-points/utils.test.ts b/public/app/features/alerting/unified/components/contact-points/utils.test.ts
index e8ded92bf74..ebf064ff6b0 100644
--- a/public/app/features/alerting/unified/components/contact-points/utils.test.ts
+++ b/public/app/features/alerting/unified/components/contact-points/utils.test.ts
@@ -1,8 +1,12 @@
+import { GrafanaManagedContactPoint } from 'app/plugins/datasource/alertmanager/types';
+
+import { KnownProvenance } from '../../types/knownProvenance';
import { ReceiverTypes } from '../receivers/grafanaAppReceivers/onCall/onCall';
import { RECEIVER_META_KEY, RECEIVER_PLUGIN_META_KEY } from './constants';
import {
ReceiverConfigWithMetadata,
+ enhanceContactPointsWithMetadata,
getReceiverDescription,
isAutoGeneratedPolicy,
summarizeEmailAddresses,
@@ -128,3 +132,110 @@ describe('summarizeEmailAddresses', () => {
expect(summarizeEmailAddresses('foo@foo.com\n bar@bar.com ')).toBe(output);
});
});
+
+describe('enhanceContactPointsWithMetadata', () => {
+ it('should extract provenance from receiver configs when contact point has no provenance', () => {
+ const contactPoint: GrafanaManagedContactPoint = {
+ name: 'test-contact-point',
+ grafana_managed_receiver_configs: [
+ {
+ uid: 'test-uid',
+ name: 'test-contact-point',
+ type: 'email',
+ settings: { addresses: 'test@example.com' },
+ secureFields: {},
+ provenance: KnownProvenance.API,
+ },
+ ],
+ };
+
+ const enhanced = enhanceContactPointsWithMetadata({
+ contactPoints: [contactPoint],
+ notifiers: [],
+ status: [],
+ });
+
+ expect(enhanced[0].provenance).toBe(KnownProvenance.API);
+ });
+
+ it('should prefer contact point provenance over receiver config provenance', () => {
+ const contactPoint: GrafanaManagedContactPoint = {
+ name: 'test-contact-point',
+ provenance: KnownProvenance.File, // Provenance on contact point (from K8s)
+ grafana_managed_receiver_configs: [
+ {
+ uid: 'test-uid',
+ name: 'test-contact-point',
+ type: 'email',
+ settings: { addresses: 'test@example.com' },
+ secureFields: {},
+ provenance: KnownProvenance.API, // Different provenance on receiver config
+ },
+ ],
+ };
+
+ const enhanced = enhanceContactPointsWithMetadata({
+ contactPoints: [contactPoint],
+ notifiers: [],
+ status: [],
+ });
+
+ expect(enhanced[0].provenance).toBe(KnownProvenance.File);
+ });
+
+ it('should extract provenance from first receiver config that has it', () => {
+ const contactPoint: GrafanaManagedContactPoint = {
+ name: 'test-contact-point',
+ grafana_managed_receiver_configs: [
+ {
+ uid: 'test-uid-1',
+ name: 'test-contact-point',
+ type: 'email',
+ settings: { addresses: 'test@example.com' },
+ secureFields: {},
+ // No provenance on first receiver
+ },
+ {
+ uid: 'test-uid-2',
+ name: 'test-contact-point',
+ type: 'slack',
+ settings: { recipient: '#channel' },
+ secureFields: {},
+ provenance: KnownProvenance.ConvertedPrometheus, // Provenance on second receiver
+ },
+ ],
+ };
+
+ const enhanced = enhanceContactPointsWithMetadata({
+ contactPoints: [contactPoint],
+ notifiers: [],
+ status: [],
+ });
+
+ expect(enhanced[0].provenance).toBe(KnownProvenance.ConvertedPrometheus);
+ });
+
+ it('should have undefined provenance when neither contact point nor receiver configs have provenance', () => {
+ const contactPoint: GrafanaManagedContactPoint = {
+ name: 'test-contact-point',
+ grafana_managed_receiver_configs: [
+ {
+ uid: 'test-uid',
+ name: 'test-contact-point',
+ type: 'email',
+ settings: { addresses: 'test@example.com' },
+ secureFields: {},
+ // No provenance
+ },
+ ],
+ };
+
+ const enhanced = enhanceContactPointsWithMetadata({
+ contactPoints: [contactPoint],
+ notifiers: [],
+ status: [],
+ });
+
+ expect(enhanced[0].provenance).toBeUndefined();
+ });
+});
diff --git a/public/app/features/alerting/unified/components/contact-points/utils.ts b/public/app/features/alerting/unified/components/contact-points/utils.ts
index d2cc43901c1..d24cfc2b0af 100644
--- a/public/app/features/alerting/unified/components/contact-points/utils.ts
+++ b/public/app/features/alerting/unified/components/contact-points/utils.ts
@@ -146,9 +146,16 @@ export function enhanceContactPointsWithMetadata({
const id = getContactPointIdentifier(contactPoint);
+ // Extract provenance from contactPoint first; else, search in its receivers
+ const contactPointProvenance =
+ 'provenance' in contactPoint && contactPoint.provenance !== undefined
+ ? contactPoint.provenance
+ : receivers.find((receiver) => Boolean(receiver.provenance))?.provenance;
+
return {
...contactPoint,
id,
+ provenance: contactPointProvenance,
policies:
alertmanagerConfiguration && usedContactPointsByName && (usedContactPointsByName[contactPoint.name] ?? []),
grafana_managed_receiver_configs: receivers.map((receiver, index) => {
diff --git a/public/app/features/alerting/unified/components/mute-timings/useMuteTimings.tsx b/public/app/features/alerting/unified/components/mute-timings/useMuteTimings.tsx
index 94e290a2087..ce0871ac2cd 100644
--- a/public/app/features/alerting/unified/components/mute-timings/useMuteTimings.tsx
+++ b/public/app/features/alerting/unified/components/mute-timings/useMuteTimings.tsx
@@ -9,7 +9,7 @@ import {
IoK8SApimachineryPkgApisMetaV1ObjectMeta,
} from 'app/features/alerting/unified/openapi/timeIntervalsApi.gen';
import { BaseAlertmanagerArgs, Skippable } from 'app/features/alerting/unified/types/hooks';
-import { PROVENANCE_NONE } from 'app/features/alerting/unified/utils/k8s/constants';
+import { KnownProvenance } from 'app/features/alerting/unified/types/knownProvenance';
import {
isK8sEntityProvisioned,
shouldUseK8sApi,
@@ -62,7 +62,7 @@ const parseAmTimeInterval: (interval: MuteTimeInterval, provenance: string) => M
return {
...interval,
id: interval.name,
- provisioned: Boolean(provenance && provenance !== PROVENANCE_NONE),
+ provisioned: Boolean(provenance && provenance !== KnownProvenance.None),
};
};
diff --git a/public/app/features/alerting/unified/components/notification-policies/NotificationPoliciesList.tsx b/public/app/features/alerting/unified/components/notification-policies/NotificationPoliciesList.tsx
index 448c409b673..5e85c5f575c 100644
--- a/public/app/features/alerting/unified/components/notification-policies/NotificationPoliciesList.tsx
+++ b/public/app/features/alerting/unified/components/notification-policies/NotificationPoliciesList.tsx
@@ -11,7 +11,7 @@ import { AlertmanagerAction, useAlertmanagerAbility } from 'app/features/alertin
import { FormAmRoute } from 'app/features/alerting/unified/types/amroutes';
import { addUniqueIdentifierToRoute } from 'app/features/alerting/unified/utils/amroutes';
import { getErrorCode, stringifyErrorLike } from 'app/features/alerting/unified/utils/misc';
-import { ObjectMatcher, ROUTES_META_SYMBOL, RouteWithID } from 'app/plugins/datasource/alertmanager/types';
+import { ObjectMatcher, RouteWithID } from 'app/plugins/datasource/alertmanager/types';
import { anyOfRequestState, isError } from '../../hooks/useAsync';
import { useAlertmanager } from '../../state/AlertmanagerContext';
@@ -27,6 +27,7 @@ import { useAddPolicyModal, useAlertGroupsModal, useDeletePolicyModal, useEditPo
import { Policy } from './Policy';
import { TIMING_OPTIONS_DEFAULTS } from './timingOptions';
import {
+ isRouteProvisioned,
useAddNotificationPolicy,
useDeleteNotificationPolicy,
useNotificationPolicyRoute,
@@ -99,6 +100,8 @@ export const NotificationPoliciesList = () => {
}
return;
}, [defaultPolicy]);
+ const routeProvenance = defaultPolicy?.provenance;
+ const isRootRouteProvisioned = rootRoute ? isRouteProvisioned(rootRoute) : false;
// useAsync could also work but it's hard to wait until it's done in the tests
// Combining with useEffect gives more predictable results because the condition is in useEffect
@@ -244,7 +247,8 @@ export const NotificationPoliciesList = () => {
currentRoute={defaults(rootRoute, TIMING_OPTIONS_DEFAULTS)}
contactPointsState={contactPointsState.receivers}
readOnly={!hasConfigurationAPI}
- provisioned={rootRoute[ROUTES_META_SYMBOL]?.provisioned}
+ provisioned={isRootRouteProvisioned}
+ provenance={routeProvenance}
alertManagerSourceName={selectedAlertmanager}
onAddPolicy={openAddModal}
onEditPolicy={openEditModal}
diff --git a/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx b/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx
index a10bca42100..62f9a57ad73 100644
--- a/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx
+++ b/public/app/features/alerting/unified/components/notification-policies/Policy.test.tsx
@@ -17,6 +17,7 @@ import {
import { useAlertmanagerAbilities } from '../../hooks/useAbilities';
import { mockReceiversState } from '../../mocks';
import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
+import { KnownProvenance } from '../../types/knownProvenance';
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
import {
@@ -331,6 +332,60 @@ describe('Policy', () => {
const customPolicy = screen.getByTestId('am-route-container');
expect(within(customPolicy).getByTestId('matches-all')).toBeInTheDocument();
});
+
+ it('shows correct badge when policy has file provenance', () => {
+ const mockRoute: RouteWithID = {
+ id: 'test-route',
+ receiver: 'test-receiver',
+ routes: [],
+ };
+
+ renderPolicy(
+
+ );
+
+ const badge = screen.getByText('Provisioned');
+ expect(badge).toBeInTheDocument();
+ });
+
+ it('shows correct badge when policy has converted_prometheus provenance', () => {
+ const mockRoute: RouteWithID = {
+ id: 'test-route',
+ receiver: 'test-receiver',
+ routes: [],
+ };
+
+ renderPolicy(
+
+ );
+
+ const badge = screen.getByText('Imported');
+ expect(badge).toBeInTheDocument();
+ });
});
// Doesn't matter which path the routes use, it just needs to match the initialEntries history entry to render the element
diff --git a/public/app/features/alerting/unified/components/notification-policies/Policy.tsx b/public/app/features/alerting/unified/components/notification-policies/Policy.tsx
index c4b6a0c55b7..d638273e006 100644
--- a/public/app/features/alerting/unified/components/notification-policies/Policy.tsx
+++ b/public/app/features/alerting/unified/components/notification-policies/Policy.tsx
@@ -61,6 +61,7 @@ interface PolicyComponentProps {
contactPointsState?: ReceiversState;
readOnly?: boolean;
provisioned?: boolean;
+ provenance?: string;
inheritedProperties?: InheritableProperties;
routesMatchingFilters?: RoutesMatchingFilters;
@@ -89,6 +90,7 @@ const Policy = (props: PolicyComponentProps) => {
contactPointsState,
readOnly = false,
provisioned = false,
+ provenance,
alertManagerSourceName,
currentRoute,
inheritedProperties,
@@ -255,7 +257,7 @@ const Policy = (props: PolicyComponentProps) => {
{/* TODO maybe we should move errors to the gutter instead? */}
{errors.length > 0 && }
- {provisioned && }
+ {provisioned && }
{!isAutoGenerated && !readOnly && (
diff --git a/public/app/features/alerting/unified/components/notification-policies/useNotificationPolicyRoute.test.tsx b/public/app/features/alerting/unified/components/notification-policies/useNotificationPolicyRoute.test.tsx
index 512f8f2ac66..a57886534ff 100644
--- a/public/app/features/alerting/unified/components/notification-policies/useNotificationPolicyRoute.test.tsx
+++ b/public/app/features/alerting/unified/components/notification-policies/useNotificationPolicyRoute.test.tsx
@@ -1,9 +1,15 @@
import { MatcherOperator, ROUTES_META_SYMBOL, Route } from 'app/plugins/datasource/alertmanager/types';
import { ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Route } from '../../openapi/routesApi.gen';
+import { KnownProvenance } from '../../types/knownProvenance';
import { ROOT_ROUTE_NAME } from '../../utils/k8s/constants';
-import { createKubernetesRoutingTreeSpec, k8sSubRouteToRoute, routeToK8sSubRoute } from './useNotificationPolicyRoute';
+import {
+ createKubernetesRoutingTreeSpec,
+ isRouteProvisioned,
+ k8sSubRouteToRoute,
+ routeToK8sSubRoute,
+} from './useNotificationPolicyRoute';
test('k8sSubRouteToRoute', () => {
const input: ComGithubGrafanaGrafanaPkgApisAlertingNotificationsV0Alpha1Route = {
@@ -115,3 +121,86 @@ test('createKubernetesRoutingTreeSpec', () => {
expect(tree.metadata.name).toBe(ROOT_ROUTE_NAME);
expect(tree).toMatchSnapshot();
});
+
+describe('isRouteProvisioned', () => {
+ it('returns false when route has no provenance', () => {
+ const route: Route = {
+ receiver: 'test-receiver',
+ };
+
+ expect(isRouteProvisioned(route)).toBeFalsy();
+ });
+
+ it('returns false when route has KnownProvenance.None in metadata', () => {
+ const route: Route = {
+ receiver: 'test-receiver',
+ [ROUTES_META_SYMBOL]: {
+ provenance: KnownProvenance.None,
+ },
+ };
+
+ expect(isRouteProvisioned(route)).toBeFalsy();
+ });
+
+ it('returns false when route has KnownProvenance.None at top level', () => {
+ const route: Route = {
+ receiver: 'test-receiver',
+ provenance: KnownProvenance.None,
+ };
+ expect(isRouteProvisioned(route)).toBeFalsy();
+ });
+
+ it('returns true when route has file provenance in metadata', () => {
+ const route: Route = {
+ receiver: 'test-receiver',
+ [ROUTES_META_SYMBOL]: {
+ provenance: KnownProvenance.File,
+ },
+ };
+
+ expect(isRouteProvisioned(route)).toBeTruthy();
+ });
+
+ it('returns true when route has api provenance in metadata', () => {
+ const route: Route = {
+ receiver: 'test-receiver',
+ [ROUTES_META_SYMBOL]: {
+ provenance: KnownProvenance.API,
+ },
+ };
+
+ expect(isRouteProvisioned(route)).toBeTruthy();
+ });
+
+ it('returns true when route has converted_prometheus provenance in metadata', () => {
+ const route: Route = {
+ receiver: 'test-receiver',
+ [ROUTES_META_SYMBOL]: {
+ provenance: KnownProvenance.ConvertedPrometheus,
+ },
+ };
+
+ expect(isRouteProvisioned(route)).toBeTruthy();
+ });
+
+ it('returns true when route has file provenance at top level', () => {
+ const route: Route = {
+ receiver: 'test-receiver',
+ provenance: KnownProvenance.File,
+ };
+
+ expect(isRouteProvisioned(route)).toBeTruthy();
+ });
+
+ it('falls back to top-level provenance when metadata provenance is missing', () => {
+ const route: Route = {
+ receiver: 'test-receiver',
+ provenance: KnownProvenance.File,
+ [ROUTES_META_SYMBOL]: {
+ provenance: undefined,
+ },
+ };
+
+ expect(isRouteProvisioned(route)).toBeTruthy();
+ });
+});
diff --git a/public/app/features/alerting/unified/components/notification-policies/useNotificationPolicyRoute.ts b/public/app/features/alerting/unified/components/notification-policies/useNotificationPolicyRoute.ts
index e6a0b7a0cc5..ca9be820463 100644
--- a/public/app/features/alerting/unified/components/notification-policies/useNotificationPolicyRoute.ts
+++ b/public/app/features/alerting/unified/components/notification-policies/useNotificationPolicyRoute.ts
@@ -22,8 +22,8 @@ import {
} from '../../reducers/alertmanager/notificationPolicyRoutes';
import { FormAmRoute } from '../../types/amroutes';
import { addUniqueIdentifierToRoute } from '../../utils/amroutes';
-import { PROVENANCE_NONE, ROOT_ROUTE_NAME } from '../../utils/k8s/constants';
-import { isK8sEntityProvisioned, shouldUseK8sApi } from '../../utils/k8s/utils';
+import { K8sAnnotations, ROOT_ROUTE_NAME } from '../../utils/k8s/constants';
+import { getAnnotation, isProvisionedResource, shouldUseK8sApi } from '../../utils/k8s/utils';
import { routeAdapter } from '../../utils/routeAdapter';
import {
InsertPosition,
@@ -33,6 +33,11 @@ import {
omitRouteFromRouteTree,
} from '../../utils/routeTree';
+export function isRouteProvisioned(route: Route): boolean {
+ const provenance = route[ROUTES_META_SYMBOL]?.provenance ?? route.provenance;
+ return isProvisionedResource(provenance);
+}
+
const k8sRoutesToRoutesMemoized = memoize(k8sRoutesToRoutes, { maxSize: 1 });
const {
@@ -82,7 +87,7 @@ const parseAmConfigRoute = memoize((route: Route): Route => {
return {
...route,
[ROUTES_META_SYMBOL]: {
- provisioned: Boolean(route.provenance && route.provenance !== PROVENANCE_NONE),
+ provenance: route.provenance,
},
};
});
@@ -232,10 +237,11 @@ function k8sRoutesToRoutes(routes: ComGithubGrafanaGrafanaPkgApisAlertingNotific
...route.spec.defaults,
routes: route.spec.routes?.map(k8sSubRouteToRoute),
[ROUTES_META_SYMBOL]: {
- provisioned: isK8sEntityProvisioned(route),
+ provenance: getAnnotation(route, K8sAnnotations.Provenance),
resourceVersion: route.metadata.resourceVersion,
name: route.metadata.name,
},
+ provenance: getAnnotation(route, K8sAnnotations.Provenance),
};
});
}
diff --git a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx
index 5a50ab55cd4..92872e56bef 100644
--- a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx
+++ b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx
@@ -33,6 +33,7 @@ import { AccessControlAction } from 'app/types/accessControl';
import { AITemplateButtonComponent } from '../../enterprise-components/AI/AIGenTemplateButton/addAITemplateButton';
import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
+import { isProvisionedResource } from '../../utils/k8s/utils';
import { makeAMLink, stringifyErrorLike } from '../../utils/misc';
import { EditorColumnHeader } from '../EditorColumnHeader';
import { ProvisionedResource, ProvisioningAlert } from '../Provisioning';
@@ -122,7 +123,8 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props)
// AI feedback state
const [aiGeneratedTemplate, setAiGeneratedTemplate] = useState(false);
- const { isProvisioned } = useNotificationTemplateMetadata(originalTemplate);
+ const { provenance } = useNotificationTemplateMetadata(originalTemplate);
+ const isProvisioned = isProvisionedResource(provenance);
const originalTemplatePrefill: TemplateFormValues | undefined = originalTemplate
? { title: originalTemplate.title, content: originalTemplate.content }
: undefined;
diff --git a/public/app/features/alerting/unified/components/receivers/TemplatesTable.test.tsx b/public/app/features/alerting/unified/components/receivers/TemplatesTable.test.tsx
new file mode 100644
index 00000000000..f707d1d6b79
--- /dev/null
+++ b/public/app/features/alerting/unified/components/receivers/TemplatesTable.test.tsx
@@ -0,0 +1,98 @@
+import { render, screen, within } from 'test/test-utils';
+
+import { AppNotificationList } from 'app/core/components/AppNotifications/AppNotificationList';
+import { AccessControlAction } from 'app/types/accessControl';
+
+import { setupMswServer } from '../../mockApi';
+import { grantUserPermissions } from '../../mocks';
+import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
+import { KnownProvenance } from '../../types/knownProvenance';
+import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
+import { NotificationTemplate } from '../contact-points/useNotificationTemplates';
+
+import { TemplatesTable } from './TemplatesTable';
+
+const mockTemplates: Array> = [
+ {
+ uid: 'mimir-template',
+ title: 'mimir-template',
+ content: '{{ define "mimir-template" }}Template from Mimir{{ end }}',
+ provenance: KnownProvenance.ConvertedPrometheus,
+ },
+ {
+ uid: 'file-template',
+ title: 'file-template',
+ content: '{{ define "file-template" }}File provisioned template{{ end }}',
+ provenance: KnownProvenance.File,
+ },
+ {
+ uid: 'api-template',
+ title: 'api-template',
+ content: '{{ define "api-template" }}API provisioned template{{ end }}',
+ provenance: KnownProvenance.API,
+ },
+ {
+ uid: 'no-provenance-template',
+ title: 'no-provenance-template',
+ content: '{{ define "no-provenance-template" }}No provenance template{{ end }}',
+ provenance: KnownProvenance.None,
+ },
+ {
+ uid: 'undefined-provenance-template',
+ title: 'undefined-provenance-template',
+ content: '{{ define "undefined-provenance-template" }}Undefined provenance template{{ end }}',
+ provenance: undefined,
+ },
+];
+
+const renderWithProvider = (templates: Array>) => {
+ return render(
+
+
+
+
+ );
+};
+
+setupMswServer();
+
+describe('TemplatesTable', () => {
+ beforeEach(() => {
+ grantUserPermissions([
+ AccessControlAction.AlertingNotificationsRead,
+ AccessControlAction.AlertingNotificationsWrite,
+ AccessControlAction.AlertingNotificationsExternalRead,
+ AccessControlAction.AlertingNotificationsExternalWrite,
+ ]);
+ });
+
+ it('shows "Imported" badge for templates with converted_prometheus provenance', () => {
+ const templates = [mockTemplates[0]]; // mimir-template
+ renderWithProvider(templates);
+
+ const templateRow = screen.getByRole('row', { name: /mimir-template/i });
+ const badge = within(templateRow).getByText('Imported');
+ expect(badge).toBeInTheDocument();
+ });
+
+ it('shows "Provisioned" badge for templates with other provenance', () => {
+ // api and file templates
+ [mockTemplates[1], mockTemplates[2]].forEach((template) => {
+ renderWithProvider([template]);
+
+ const templateRow = screen.getByRole('row', { name: new RegExp(template.title ?? '', 'i') });
+ const badge = within(templateRow).getByText('Provisioned');
+ expect(badge).toBeInTheDocument();
+ });
+ });
+
+ it('does not show badge for templates with KnownProvenance.None or empty string provenance', () => {
+ // no-provenance-template and undefined-provenance-template
+ [mockTemplates[3], mockTemplates[4]].forEach((template) => {
+ renderWithProvider([template]);
+
+ const templateRow = screen.getByRole('row', { name: new RegExp(template.title ?? '', 'i') });
+ expect(within(templateRow).queryByText('Provisioned')).not.toBeInTheDocument();
+ });
+ });
+});
diff --git a/public/app/features/alerting/unified/components/receivers/TemplatesTable.tsx b/public/app/features/alerting/unified/components/receivers/TemplatesTable.tsx
index ea00e22b280..4f71904dd73 100644
--- a/public/app/features/alerting/unified/components/receivers/TemplatesTable.tsx
+++ b/public/app/features/alerting/unified/components/receivers/TemplatesTable.tsx
@@ -10,6 +10,7 @@ import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/d
import { Authorize } from '../../components/Authorize';
import { AlertmanagerAction } from '../../hooks/useAbilities';
import { getAlertTableStyles } from '../../styles/table';
+import { isProvisionedResource } from '../../utils/k8s/utils';
import { makeAMLink, stringifyErrorLike } from '../../utils/misc';
import { CollapseToggle } from '../CollapseToggle';
import { DetailsField } from '../DetailsField';
@@ -128,7 +129,8 @@ function TemplateRow({ notificationTemplate, idx, alertManagerName, onDeleteClic
const isGrafanaAlertmanager = alertManagerName === GRAFANA_RULES_SOURCE_NAME;
const [isExpanded, setIsExpanded] = useState(false);
- const { isProvisioned } = useNotificationTemplateMetadata(notificationTemplate);
+ const { provenance } = useNotificationTemplateMetadata(notificationTemplate);
+ const isProvisioned = isProvisionedResource(provenance);
const { uid, title: name, content: template, missing } = notificationTemplate;
const misconfiguredBadgeText = t('alerting.templates.misconfigured-badge-text', 'Misconfigured');
@@ -139,7 +141,7 @@ function TemplateRow({ notificationTemplate, idx, alertManagerName, onDeleteClic
setIsExpanded(!isExpanded)} />
- {name} {isProvisioned && }{' '}
+ {name} {isProvisioned && }{' '}
{missing && !isGrafanaAlertmanager && (
;
secureFields: Record;
+ version?: string;
};
type TestReceiverFormValues = {
@@ -246,4 +248,241 @@ describe('ChannelSubForm', () => {
expect(slackUrl).toBeEnabled();
expect(slackUrl).toHaveValue('');
});
+
+ describe('version-specific options display', () => {
+ // Create a mock notifier with different options for v0 and v1
+ const legacyOptions = [
+ {
+ element: 'input' as const,
+ inputType: 'text',
+ label: 'Legacy URL',
+ description: 'The legacy endpoint URL',
+ placeholder: '',
+ propertyName: 'legacyUrl',
+ required: true,
+ secure: false,
+ showWhen: { field: '', is: '' },
+ validationRule: '',
+ dependsOn: '',
+ },
+ ];
+
+ const webhookWithVersions: NotifierDTO = {
+ ...grafanaAlertNotifiers.webhook,
+ versions: [
+ {
+ version: 'v0mimir1',
+ label: 'Webhook (Legacy)',
+ description: 'Legacy webhook from Mimir',
+ canCreate: false,
+ options: legacyOptions,
+ },
+ {
+ version: 'v0mimir2',
+ label: 'Webhook (Legacy v2)',
+ description: 'Legacy webhook v2 from Mimir',
+ canCreate: false,
+ options: legacyOptions,
+ },
+ {
+ version: 'v1',
+ label: 'Webhook',
+ description: 'Sends HTTP POST request',
+ canCreate: true,
+ options: grafanaAlertNotifiers.webhook.options,
+ },
+ ],
+ };
+
+ const versionedNotifiers: Notifier[] = [
+ { dto: webhookWithVersions, meta: { enabled: true, order: 1 } },
+ { dto: grafanaAlertNotifiers.slack, meta: { enabled: true, order: 2 } },
+ ];
+
+ function VersionedTestFormWrapper({
+ defaults,
+ initial,
+ }: {
+ defaults: TestChannelValues;
+ initial?: TestChannelValues;
+ }) {
+ const form = useForm({
+ defaultValues: {
+ name: 'test-contact-point',
+ items: [defaults],
+ },
+ });
+
+ return (
+
+
+
+
+
+ );
+ }
+
+ function renderVersionedForm(defaults: TestChannelValues, initial?: TestChannelValues) {
+ return render();
+ }
+
+ it('should display v1 options when integration has v1 version', () => {
+ const webhookV1: TestChannelValues = {
+ __id: 'id-0',
+ type: 'webhook',
+ version: 'v1',
+ settings: { url: 'https://example.com' },
+ secureFields: {},
+ };
+
+ renderVersionedForm(webhookV1, webhookV1);
+
+ // Should show v1 URL field (from default options)
+ expect(ui.settings.webhook.url.get()).toBeInTheDocument();
+ // Should NOT show legacy URL field
+ expect(screen.queryByRole('textbox', { name: /Legacy URL/i })).not.toBeInTheDocument();
+ });
+
+ it('should display v0 options when integration has legacy version', () => {
+ const webhookV0: TestChannelValues = {
+ __id: 'id-0',
+ type: 'webhook',
+ version: 'v0mimir1',
+ settings: { legacyUrl: 'https://legacy.example.com' },
+ secureFields: {},
+ };
+
+ renderVersionedForm(webhookV0, webhookV0);
+
+ // Should show legacy URL field (from v0 options)
+ expect(screen.getByRole('textbox', { name: /Legacy URL/i })).toBeInTheDocument();
+ // Should NOT show v1 URL field
+ expect(ui.settings.webhook.url.query()).not.toBeInTheDocument();
+ });
+
+ it('should display "Legacy" badge for v0mimir1 integration', () => {
+ const webhookV0: TestChannelValues = {
+ __id: 'id-0',
+ type: 'webhook',
+ version: 'v0mimir1',
+ settings: { legacyUrl: 'https://legacy.example.com' },
+ secureFields: {},
+ };
+
+ renderVersionedForm(webhookV0, webhookV0);
+
+ // Should show "Legacy" badge for v0mimir1 integrations
+ expect(screen.getByText('Legacy')).toBeInTheDocument();
+ });
+
+ it('should display "Legacy v2" badge for v0mimir2 integration', () => {
+ const webhookV0v2: TestChannelValues = {
+ __id: 'id-0',
+ type: 'webhook',
+ version: 'v0mimir2',
+ settings: { legacyUrl: 'https://legacy.example.com' },
+ secureFields: {},
+ };
+
+ renderVersionedForm(webhookV0v2, webhookV0v2);
+
+ // Should show "Legacy v2" badge for v0mimir2 integrations
+ expect(screen.getByText('Legacy v2')).toBeInTheDocument();
+ });
+
+ it('should NOT display version badge for v1 integration', () => {
+ const webhookV1: TestChannelValues = {
+ __id: 'id-0',
+ type: 'webhook',
+ version: 'v1',
+ settings: { url: 'https://example.com' },
+ secureFields: {},
+ };
+
+ renderVersionedForm(webhookV1, webhookV1);
+
+ // Should NOT show version badge for non-legacy v1 integrations
+ expect(screen.queryByText('v1')).not.toBeInTheDocument();
+ });
+
+ it('should filter out notifiers with canCreate: false from dropdown', () => {
+ // Create a notifier that only has v0 versions (cannot be created)
+ const legacyOnlyNotifier: NotifierDTO = {
+ type: 'wechat',
+ name: 'WeChat',
+ heading: 'WeChat settings',
+ description: 'Sends notifications to WeChat',
+ options: [],
+ versions: [
+ {
+ version: 'v0mimir1',
+ label: 'WeChat (Legacy)',
+ description: 'Legacy WeChat',
+ canCreate: false,
+ options: [],
+ },
+ ],
+ };
+
+ const notifiersWithLegacyOnly: Notifier[] = [
+ { dto: webhookWithVersions, meta: { enabled: true, order: 1 } },
+ { dto: legacyOnlyNotifier, meta: { enabled: true, order: 2 } },
+ ];
+
+ function LegacyOnlyTestWrapper({ defaults }: { defaults: TestChannelValues }) {
+ const form = useForm({
+ defaultValues: {
+ name: 'test-contact-point',
+ items: [defaults],
+ },
+ });
+
+ return (
+
+
+
+
+
+ );
+ }
+
+ render(
+
+ );
+
+ // Webhook should be in dropdown (has v1 with canCreate: true)
+ expect(ui.typeSelector.get()).toHaveTextContent('Webhook');
+
+ // WeChat should NOT be in the options (only has v0 with canCreate: false)
+ // We can't easily check dropdown options without opening it, but the filter should work
+ });
+ });
});
diff --git a/public/app/features/alerting/unified/components/receivers/form/ChannelSubForm.tsx b/public/app/features/alerting/unified/components/receivers/form/ChannelSubForm.tsx
index c49b5184623..cb1d79025f8 100644
--- a/public/app/features/alerting/unified/components/receivers/form/ChannelSubForm.tsx
+++ b/public/app/features/alerting/unified/components/receivers/form/ChannelSubForm.tsx
@@ -6,7 +6,7 @@ import { Controller, FieldErrors, useFormContext } from 'react-hook-form';
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
-import { Alert, Button, Field, Select, Stack, Text, useStyles2 } from '@grafana/ui';
+import { Alert, Badge, Button, Field, Select, Stack, Text, useStyles2 } from '@grafana/ui';
import { NotificationChannelOption } from 'app/features/alerting/unified/types/alerting';
import {
@@ -16,6 +16,12 @@ import {
GrafanaChannelValues,
ReceiverFormValues,
} from '../../../types/receiver-form';
+import {
+ canCreateNotifier,
+ getLegacyVersionLabel,
+ getOptionsForVersion,
+ isLegacyVersion,
+} from '../../../utils/notifier-versions';
import { OnCallIntegrationType } from '../grafanaAppReceivers/onCall/useOnCallIntegration';
import { ChannelOptions } from './ChannelOptions';
@@ -62,6 +68,7 @@ export function ChannelSubForm({
const channelFieldPath = `items.${integrationIndex}` as const;
const typeFieldPath = `${channelFieldPath}.type` as const;
+ const versionFieldPath = `${channelFieldPath}.version` as const;
const settingsFieldPath = `${channelFieldPath}.settings` as const;
const secureFieldsPath = `${channelFieldPath}.secureFields` as const;
@@ -104,6 +111,9 @@ export function ChannelSubForm({
setValue(settingsFieldPath, defaultNotifierSettings);
setValue(secureFieldsPath, {});
+
+ // Reset version when changing type - backend will use its default
+ setValue(versionFieldPath, undefined);
}
// Restore initial value of an existing oncall integration
@@ -123,6 +133,7 @@ export function ChannelSubForm({
setValue,
settingsFieldPath,
typeFieldPath,
+ versionFieldPath,
secureFieldsPath,
getValues,
watch,
@@ -164,24 +175,30 @@ export function ChannelSubForm({
setValue(`${settingsFieldPath}.${fieldPath}`, undefined);
};
- const typeOptions = useMemo(
- (): SelectableValue[] =>
- sortBy(notifiers, ({ dto, meta }) => [meta?.order ?? 0, dto.name]).map(
- ({ dto: { name, type }, meta }) => ({
- // @ts-expect-error ReactNode is supported
+ const typeOptions = useMemo((): SelectableValue[] => {
+ // Filter out notifiers that can't be created (e.g., v0-only integrations like WeChat)
+ // These are legacy integrations that only exist in Mimir and can't be created in Grafana
+ const creatableNotifiers = notifiers.filter(({ dto }) => canCreateNotifier(dto));
+
+ return sortBy(creatableNotifiers, ({ dto, meta }) => [meta?.order ?? 0, dto.name]).map(
+ ({ dto: { name, type }, meta }) => {
+ return {
+ // ReactNode is supported in Select label, but types don't reflect it
+ /* eslint-disable @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any */
label: (
{name}
{meta?.badge}
- ),
+ ) as any,
+ /* eslint-enable @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any */
value: type,
description: meta?.description,
isDisabled: meta ? !meta.enabled : false,
- })
- ),
- [notifiers]
- );
+ };
+ }
+ );
+ }, [notifiers]);
const handleTest = async () => {
await trigger();
@@ -198,10 +215,21 @@ export function ChannelSubForm({
// Cloud AM takes no value at all
const isParseModeNone = parse_mode === 'None' || !parse_mode;
const showTelegramWarning = isTelegram && !isParseModeNone;
+
+ // Check if current integration is a legacy version (canCreate: false)
+ // Legacy integrations are read-only and cannot be edited
+ // Read version from existing integration data (stored in receiver config)
+ const integrationVersion = initialValues?.version || defaultValues.version;
+ const isLegacy = notifier ? isLegacyVersion(notifier.dto, integrationVersion) : false;
+
+ // Get the correct options based on the integration's version
+ // This ensures legacy (v0) integrations display the correct schema
+ const versionedOptions = notifier ? getOptionsForVersion(notifier.dto, integrationVersion) : [];
+
// if there are mandatory options defined, optional options will be hidden by a collapse
// if there aren't mandatory options, all options will be shown without collapse
- const mandatoryOptions = notifier?.dto.options.filter((o) => o.required) ?? [];
- const optionalOptions = notifier?.dto.options.filter((o) => !o.required) ?? [];
+ const mandatoryOptions = versionedOptions.filter((o) => o.required);
+ const optionalOptions = versionedOptions.filter((o) => !o.required);
const contactPointTypeInputId = `contact-point-type-${pathPrefix}`;
return (
@@ -214,21 +242,35 @@ export function ChannelSubForm({
data-testid={`${pathPrefix}type`}
noMargin
>
- (
-