From bcaf94f2191ca9d29356157f98de200a38a3540b Mon Sep 17 00:00:00 2001 From: Todd Treece <360020+toddtreece@users.noreply.github.com> Date: Thu, 4 Dec 2025 11:58:49 -0500 Subject: [PATCH 01/48] Plugins API: Add plugins to RBAC mapper (#114843) --- pkg/services/authz/rbac/mapper.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/services/authz/rbac/mapper.go b/pkg/services/authz/rbac/mapper.go index fd7e5d2140c..e34965df999 100644 --- a/pkg/services/authz/rbac/mapper.go +++ b/pkg/services/authz/rbac/mapper.go @@ -297,6 +297,10 @@ func NewMapperRegistry() MapperRegistry { skipScopeOnVerb: nil, }, }, + "plugins.grafana.app": { + "plugins": newResourceTranslation("plugins.plugins", "uid", false, nil), + "pluginsmeta": newResourceTranslation("plugins.pluginsmeta", "uid", false, nil), + }, }) return mapper From 59ec85b93698f8ee190dba10a483b12519e7efb2 Mon Sep 17 00:00:00 2001 From: Gabriel MABILLE Date: Thu, 4 Dec 2025 18:00:57 +0100 Subject: [PATCH 02/48] `grafana-iam`: Fix missing UID (#114856) --- pkg/registry/apis/iam/resourcepermission/models.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/registry/apis/iam/resourcepermission/models.go b/pkg/registry/apis/iam/resourcepermission/models.go index b922543e83d..7c734a6825e 100644 --- a/pkg/registry/apis/iam/resourcepermission/models.go +++ b/pkg/registry/apis/iam/resourcepermission/models.go @@ -17,6 +17,7 @@ import ( "github.com/grafana/grafana/pkg/registry/apis/iam/common" idStore "github.com/grafana/grafana/pkg/registry/apis/iam/legacy" "github.com/grafana/grafana/pkg/services/accesscontrol" + gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils" ) var ( @@ -120,6 +121,7 @@ func newV0ResourcePermission(grn *groupResourceName, specs []v0alpha1.ResourcePe }, } r.SetUpdateTimestamp(updated.UTC()) + r.UID = gapiutil.CalculateClusterWideUID(&r) return r } From c90677831b8b3c2025936ed12371d68246ba0804 Mon Sep 17 00:00:00 2001 From: Collin Fingar Date: Thu, 4 Dec 2025 12:21:27 -0500 Subject: [PATCH 03/48] Library Panels: Update connection generation for V2 (#114504) * Library Panels: Update connection generation for V2 * add test --------- Co-authored-by: Haris Rozajac --- pkg/services/librarypanels/librarypanels.go | 21 +++++++++++++--- .../librarypanels/librarypanels_test.go | 25 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/pkg/services/librarypanels/librarypanels.go b/pkg/services/librarypanels/librarypanels.go index e029925ed25..ca08b57dc0d 100644 --- a/pkg/services/librarypanels/librarypanels.go +++ b/pkg/services/librarypanels/librarypanels.go @@ -64,9 +64,19 @@ var _ Service = (*LibraryPanelService)(nil) // ConnectLibraryPanelsForDashboard loops through all panels in dashboard JSON and connects any library panels to the dashboard. func (lps *LibraryPanelService) ConnectLibraryPanelsForDashboard(c context.Context, signedInUser identity.Requester, dash *dashboards.Dashboard) error { - panels := dash.Data.Get("panels").MustArray() + var panels []any + isV2 := dash.Data.Get("elements").Interface() != nil + if isV2 { + elementsMap := dash.Data.Get("elements").MustMap() + panels = make([]any, 0, len(elementsMap)) + for _, element := range elementsMap { + panels = append(panels, element) + } + } else { + panels = dash.Data.Get("panels").MustArray() + } libraryPanels := make(map[string]string) - err := connectLibraryPanelsRecursively(c, panels, libraryPanels) + err := connectLibraryPanelsRecursively(c, panels, libraryPanels, isV2) if err != nil { return err } @@ -83,10 +93,13 @@ func isLibraryPanelOrRow(panel *simplejson.Json, panelType string) bool { return panel.Interface() != nil || panelType == "row" } -func connectLibraryPanelsRecursively(c context.Context, panels []any, libraryPanels map[string]string) error { +func connectLibraryPanelsRecursively(c context.Context, panels []any, libraryPanels map[string]string, isV2 bool) error { for _, panel := range panels { panelAsJSON := simplejson.NewFromAny(panel) libraryPanel := panelAsJSON.Get("libraryPanel") + if isV2 { + libraryPanel = panelAsJSON.Get("spec").Get("libraryPanel") + } panelType := panelAsJSON.Get("type").MustString() if !isLibraryPanelOrRow(libraryPanel, panelType) { continue @@ -95,7 +108,7 @@ func connectLibraryPanelsRecursively(c context.Context, panels []any, libraryPan // we have a row if panelType == "row" { rowPanels := panelAsJSON.Get("panels").MustArray() - err := connectLibraryPanelsRecursively(c, rowPanels, libraryPanels) + err := connectLibraryPanelsRecursively(c, rowPanels, libraryPanels, isV2) if err != nil { return err } diff --git a/pkg/services/librarypanels/librarypanels_test.go b/pkg/services/librarypanels/librarypanels_test.go index a5ebd74d609..d93fb00bf5e 100644 --- a/pkg/services/librarypanels/librarypanels_test.go +++ b/pkg/services/librarypanels/librarypanels_test.go @@ -87,6 +87,31 @@ func TestIntegrationConnectLibraryPanelsForDashboard(t *testing.T) { require.Equal(t, sc.initialResult.Result.UID, elements[sc.initialResult.Result.UID].UID) }) + scenarioWithLibraryPanel(t, "When an admin tries to store a V2 dashboard with a library panel, it should connect the two", + func(t *testing.T, sc scenarioContext) { + dashJSON := map[string]any{ + "elements": []any{ + map[string]any{ + "kind": "Panel", + "spec": map[string]any{ + "datasource": "${DS_GDEV-TESTDATA}", + "libraryPanel": map[string]any{ + "uid": sc.initialResult.Result.UID, + }, + }, + }, + }, + } + dash := dashboards.Dashboard{ + Title: "Testing ConnectLibraryPanelsForDashboard for V2 dashboard", + Data: simplejson.NewFromAny(dashJSON), + } + dashInDB := createDashboard(t, sc, &dash) + + err := sc.service.ConnectLibraryPanelsForDashboard(sc.ctx, sc.user, dashInDB) + require.NoError(t, err) + }) + scenarioWithLibraryPanel(t, "When an admin tries to store a dashboard with library panels inside and outside of rows, it should connect all", func(t *testing.T, sc scenarioContext) { cmd := model.CreateLibraryElementCommand{ From 42661bed36b6143f922bae4f3434137169ee86fb Mon Sep 17 00:00:00 2001 From: Costa Alexoglou Date: Thu, 4 Dec 2025 18:27:49 +0100 Subject: [PATCH 04/48] feat: add default permissions and DTO support for MT (#114829) * feat: add default permissions and DTO support for MT * chore: review comments * chore: review comments --- pkg/registry/apis/dashboard/register.go | 103 +++++++++++++++++-- pkg/registry/apis/dashboard/sub_dto.go | 127 ++++++++++++++---------- 2 files changed, 172 insertions(+), 58 deletions(-) diff --git a/pkg/registry/apis/dashboard/register.go b/pkg/registry/apis/dashboard/register.go index d98407a1f57..16873ec2101 100644 --- a/pkg/registry/apis/dashboard/register.go +++ b/pkg/registry/apis/dashboard/register.go @@ -64,6 +64,7 @@ import ( "github.com/grafana/grafana/pkg/storage/legacysql/dualwrite" "github.com/grafana/grafana/pkg/storage/unified/apistore" "github.com/grafana/grafana/pkg/storage/unified/resource" + resourcepb "github.com/grafana/grafana/pkg/storage/unified/resourcepb" "github.com/grafana/grafana/pkg/util" ) @@ -531,11 +532,9 @@ func (b *DashboardsAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver RequireDeprecatedInternalID: true, } - // TODO: merge this into one option if b.isStandalone { - // TODO: Sets default root permissions + storageOpts.Permissions = b.setDefaultDashboardPermissions } else { - // Sets default root permissions storageOpts.Permissions = b.dashboardPermissions.SetDefaultPermissionsAfterCreate } @@ -647,6 +646,18 @@ func (b *DashboardsAPIBuilder) storageForVersion( unified.AfterDelete = b.afterDelete storage[dashboards.StoragePath()] = unified + storage[dashboards.StoragePath("dto")], err = NewDTOConnector( + unified, + largeObjects, + b.unified, + b.accessClient, + newDTOFunc, + nil, // no publicDashboardService in standalone mode + ) + if err != nil { + return err + } + return nil } @@ -675,10 +686,8 @@ func (b *DashboardsAPIBuilder) storageForVersion( storage[dashboards.StoragePath("dto")], err = NewDTOConnector( storage[dashboards.StoragePath()].(rest.Getter), largeObjects, - b.legacy.Access, b.unified, - b.accessControl, - opts.Scheme, + b.accessClient, newDTOFunc, b.publicDashboardService, ) @@ -746,6 +755,88 @@ func (b *DashboardsAPIBuilder) afterDelete(obj runtime.Object, _ *metav1.DeleteO } } +var defaultDashboardPermissions = []map[string]any{ + { + "kind": "BasicRole", + "name": "Admin", + "verb": "admin", + }, + { + "kind": "BasicRole", + "name": "Editor", + "verb": "edit", + }, + { + "kind": "BasicRole", + "name": "Viewer", + "verb": "view", + }, +} + +func (b *DashboardsAPIBuilder) setDefaultDashboardPermissions(ctx context.Context, key *resourcepb.ResourceKey, id authlib.AuthInfo, obj utils.GrafanaMetaAccessor) error { + if b.resourcePermissionsSvc == nil { + return nil + } + + if obj.GetFolder() != "" { + return nil + } + + log := logging.FromContext(ctx) + log.Debug("setting default dashboard permissions", "uid", obj.GetName(), "namespace", obj.GetNamespace()) + + client := (*b.resourcePermissionsSvc).Namespace(obj.GetNamespace()) + name := fmt.Sprintf("%s-%s-%s", dashv1.DashboardResourceInfo.GroupVersionResource().Group, dashv1.DashboardResourceInfo.GroupVersionResource().Resource, obj.GetName()) + + if _, err := client.Get(ctx, name, metav1.GetOptions{}); err == nil { + _, err := client.Update(ctx, &unstructured.Unstructured{ + Object: map[string]interface{}{ + "metadata": map[string]any{ + "name": name, + "namespace": obj.GetNamespace(), + }, + "spec": map[string]any{ + "resource": map[string]any{ + "apiGroup": dashv1.DashboardResourceInfo.GroupVersionResource().Group, + "resource": dashv1.DashboardResourceInfo.GroupVersionResource().Resource, + "name": obj.GetName(), + }, + "permissions": defaultDashboardPermissions, + }, + }, + }, metav1.UpdateOptions{}) + if err != nil { + log.Error("failed to update dashboard permissions", "error", err) + return fmt.Errorf("update dashboard permissions: %w", err) + } + + return nil + } + + _, err := client.Create(ctx, &unstructured.Unstructured{ + Object: map[string]interface{}{ + "metadata": map[string]any{ + "name": name, + "namespace": obj.GetNamespace(), + }, + "spec": map[string]any{ + "resource": map[string]any{ + "apiGroup": dashv1.DashboardResourceInfo.GroupVersionResource().Group, + "resource": dashv1.DashboardResourceInfo.GroupVersionResource().Resource, + "name": obj.GetName(), + }, + "permissions": defaultDashboardPermissions, + }, + }, + }, metav1.CreateOptions{}) + if err != nil { + log.Error("failed to create dashboard permissions", "error", err) + return fmt.Errorf("create dashboard permissions: %w", err) + } + + return nil +} + func (b *DashboardsAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions { return func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { defs := dashv0.GetOpenAPIDefinitions(ref) diff --git a/pkg/registry/apis/dashboard/sub_dto.go b/pkg/registry/apis/dashboard/sub_dto.go index 04a14387fc7..d774806147c 100644 --- a/pkg/registry/apis/dashboard/sub_dto.go +++ b/pkg/registry/apis/dashboard/sub_dto.go @@ -12,17 +12,17 @@ import ( authlib "github.com/grafana/authlib/types" "github.com/grafana/grafana-app-sdk/logging" "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard" + dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1" "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" "github.com/grafana/grafana/pkg/infra/slugify" - "github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy" - "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/publicdashboards" "github.com/grafana/grafana/pkg/storage/unified/apistore" "github.com/grafana/grafana/pkg/storage/unified/resource" "github.com/grafana/grafana/pkg/storage/unified/resourcepb" + "github.com/grafana/grafana/pkg/util" ) type dtoBuilder = func(dashboard runtime.Object, access *dashboard.DashboardAccess) (runtime.Object, error) @@ -30,11 +30,9 @@ type dtoBuilder = func(dashboard runtime.Object, access *dashboard.DashboardAcce // The DTO returns everything the UI needs in a single request type DTOConnector struct { getter rest.Getter - legacy legacy.DashboardAccessor unified resource.ResourceClient largeObjects apistore.LargeObjectSupport - accessControl accesscontrol.AccessControl - scheme *runtime.Scheme + accessClient authlib.AccessClient builder dtoBuilder publicDashboardService publicdashboards.Service } @@ -42,21 +40,17 @@ type DTOConnector struct { func NewDTOConnector( getter rest.Getter, largeObjects apistore.LargeObjectSupport, - legacyAccess legacy.DashboardAccessor, resourceClient resource.ResourceClient, - accessControl accesscontrol.AccessControl, - scheme *runtime.Scheme, + accessClient authlib.AccessClient, builder dtoBuilder, publicDashboardService publicdashboards.Service, ) (rest.Storage, error) { return &DTOConnector{ getter: getter, - legacy: legacyAccess, - accessControl: accessControl, + accessClient: accessClient, unified: resourceClient, largeObjects: largeObjects, builder: builder, - scheme: scheme, publicDashboardService: publicDashboardService, }, nil } @@ -132,35 +126,87 @@ func (r *DTOConnector) Connect(ctx context.Context, name string, opts runtime.Ob return } - dashScope := dashboards.ScopeDashboardsProvider.GetResourceScopeUID(name) - evaluator := accesscontrol.EvalPermission(dashboards.ActionDashboardsRead, dashScope) - canView, err := r.accessControl.Evaluate(ctx, user, evaluator) - if err != nil || !canView { + logger := logging.FromContext(ctx).With("logger", "dto-connector") + access := &dashboard.DashboardAccess{} + folder := obj.GetFolder() + ns := obj.GetNamespace() + + authInfo, ok := authlib.AuthInfoFrom(ctx) + if !ok { + responder.Error(fmt.Errorf("no identity found for request")) + return + } + + gvr := dashv1.DashboardResourceInfo.GroupVersionResource() + + // Check read permission using authlib.AccessClient + readRes, err := r.accessClient.Check(ctx, authInfo, authlib.CheckRequest{ + Verb: utils.VerbGet, + Group: gvr.Group, + Resource: gvr.Resource, + Namespace: ns, + Name: name, + }, folder) + if err != nil { + logger.Warn("Failed to check read permission", "err", err) + responder.Error(fmt.Errorf("not allowed to view")) + return + } + if !readRes.Allowed { responder.Error(fmt.Errorf("not allowed to view")) return } - access := &dashboard.DashboardAccess{} - writeEvaluator := accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, dashScope) - access.CanSave, _ = r.accessControl.Evaluate(ctx, user, writeEvaluator) - access.CanEdit = access.CanSave - adminEvaluator := accesscontrol.EvalPermission(dashboards.ActionDashboardsPermissionsWrite, dashScope) - access.CanAdmin, _ = r.accessControl.Evaluate(ctx, user, adminEvaluator) - deleteEvaluator := accesscontrol.EvalPermission(dashboards.ActionDashboardsDelete, dashScope) - access.CanDelete, _ = r.accessControl.Evaluate(ctx, user, deleteEvaluator) + // Check write permission + writeRes, err := r.accessClient.Check(ctx, authInfo, authlib.CheckRequest{ + Verb: utils.VerbUpdate, + Group: gvr.Group, + Resource: gvr.Resource, + Namespace: ns, + Name: name, + }, folder) + // Keeping the same logic as with accessControl.Evaluate. + // On errors we default on deny. + if err != nil { + logger.Warn("Failed to check write permission", "err", err) + } + access.CanSave = writeRes.Allowed + access.CanEdit = writeRes.Allowed + + // Check delete permission + deleteRes, err := r.accessClient.Check(ctx, authInfo, authlib.CheckRequest{ + Verb: utils.VerbDelete, + Group: gvr.Group, + Resource: gvr.Resource, + Namespace: ns, + Name: name, + }, folder) + if err != nil { + logger.Warn("Failed to check delete permission", "err", err) + } + access.CanDelete = deleteRes.Allowed + + // For admin permission, use write as a proxy for now + access.CanAdmin = writeRes.Allowed + access.CanStar = user.IsIdentityType(authlib.TypeUser) - access.AnnotationsPermissions = &dashboard.AnnotationPermission{} - r.getAnnotationPermissionsByScope(ctx, user, &access.AnnotationsPermissions.Dashboard, dashScope) - r.getAnnotationPermissionsByScope(ctx, user, &access.AnnotationsPermissions.Organization, accesscontrol.ScopeAnnotationsTypeOrganization) + // Annotation permissions - use write permission as proxy + access.AnnotationsPermissions = &dashboard.AnnotationPermission{ + Dashboard: dashboard.AnnotationActions{CanAdd: writeRes.Allowed, CanEdit: writeRes.Allowed, CanDelete: writeRes.Allowed}, + Organization: dashboard.AnnotationActions{CanAdd: writeRes.Allowed, CanEdit: writeRes.Allowed, CanDelete: writeRes.Allowed}, + } title := obj.FindTitle("") access.Slug = slugify.Slugify(title) access.Url = dashboards.GetDashboardFolderURL(false, name, access.Slug) - pubDash, err := r.publicDashboardService.FindByDashboardUid(ctx, user.GetOrgID(), name) - if err == nil && pubDash != nil { - access.IsPublic = true + // Only check public dashboards if service is available + if !util.IsInterfaceNil(r.publicDashboardService) { + pubDash, err := r.publicDashboardService.FindByDashboardUid(ctx, user.GetOrgID(), name) + if err == nil && pubDash != nil { + access.IsPublic = true + } } dash, err := r.builder(rawobj, access) @@ -171,26 +217,3 @@ func (r *DTOConnector) Connect(ctx context.Context, name string, opts runtime.Ob responder.Object(http.StatusOK, dash) }), nil } - -func (r *DTOConnector) getAnnotationPermissionsByScope(ctx context.Context, user identity.Requester, actions *dashboard.AnnotationActions, scope string) { - var err error - logger := logging.FromContext(ctx).With("logger", "dto-connector") - - evaluate := accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsCreate, scope) - actions.CanAdd, err = r.accessControl.Evaluate(ctx, user, evaluate) - if err != nil { - logger.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsCreate, "scope", scope) - } - - evaluate = accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsDelete, scope) - actions.CanDelete, err = r.accessControl.Evaluate(ctx, user, evaluate) - if err != nil { - logger.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsDelete, "scope", scope) - } - - evaluate = accesscontrol.EvalPermission(accesscontrol.ActionAnnotationsWrite, scope) - actions.CanEdit, err = r.accessControl.Evaluate(ctx, user, evaluate) - if err != nil { - logger.Warn("Failed to evaluate permission", "err", err, "action", accesscontrol.ActionAnnotationsWrite, "scope", scope) - } -} From 665daa5a5dcfb9e48172f6979af7d1be83a3e888 Mon Sep 17 00:00:00 2001 From: Andrew Hackmann <5140848+bossinc@users.noreply.github.com> Date: Thu, 4 Dec 2025 11:28:38 -0600 Subject: [PATCH 05/48] Elasticsearch: Client refactor (#114745) * split up client.go * split up search_request.go * remove double spaces --- .../client/aggregation_builder.go | 224 ++++++++++++ pkg/tsdb/elasticsearch/client/client.go | 309 +---------------- .../elasticsearch/client/http_transport.go | 66 ++++ .../elasticsearch/client/query_builder.go | 112 ++++++ .../elasticsearch/client/request_encoder.go | 53 +++ .../elasticsearch/client/response_parser.go | 262 ++++++++++++++ .../elasticsearch/client/search_request.go | 327 ------------------ 7 files changed, 734 insertions(+), 619 deletions(-) create mode 100644 pkg/tsdb/elasticsearch/client/aggregation_builder.go create mode 100644 pkg/tsdb/elasticsearch/client/http_transport.go create mode 100644 pkg/tsdb/elasticsearch/client/query_builder.go create mode 100644 pkg/tsdb/elasticsearch/client/request_encoder.go create mode 100644 pkg/tsdb/elasticsearch/client/response_parser.go diff --git a/pkg/tsdb/elasticsearch/client/aggregation_builder.go b/pkg/tsdb/elasticsearch/client/aggregation_builder.go new file mode 100644 index 00000000000..043a0d82f4a --- /dev/null +++ b/pkg/tsdb/elasticsearch/client/aggregation_builder.go @@ -0,0 +1,224 @@ +package es + +const ( + // DefaultGeoHashPrecision is the default precision for geohash grid aggregations + DefaultGeoHashPrecision = 3 + // termsOrderTerm is used internally for ordering terms + termsOrderTerm = "_term" +) + +// AggBuilder represents an aggregation builder +type AggBuilder interface { + Histogram(key, field string, fn func(a *HistogramAgg, b AggBuilder)) AggBuilder + DateHistogram(key, field string, fn func(a *DateHistogramAgg, b AggBuilder)) AggBuilder + Terms(key, field string, fn func(a *TermsAggregation, b AggBuilder)) AggBuilder + Nested(key, path string, fn func(a *NestedAggregation, b AggBuilder)) AggBuilder + Filters(key string, fn func(a *FiltersAggregation, b AggBuilder)) AggBuilder + GeoHashGrid(key, field string, fn func(a *GeoHashGridAggregation, b AggBuilder)) AggBuilder + Metric(key, metricType, field string, fn func(a *MetricAggregation)) AggBuilder + Pipeline(key, pipelineType string, bucketPath any, fn func(a *PipelineAggregation)) AggBuilder + Build() (AggArray, error) +} + +type aggBuilderImpl struct { + AggBuilder + aggDefs []*aggDef +} + +func newAggBuilder() *aggBuilderImpl { + return &aggBuilderImpl{ + aggDefs: make([]*aggDef, 0), + } +} + +func (b *aggBuilderImpl) Build() (AggArray, error) { + aggs := make(AggArray, 0) + + for _, aggDef := range b.aggDefs { + agg := &Agg{ + Key: aggDef.key, + Aggregation: aggDef.aggregation, + } + + for _, cb := range aggDef.builders { + childAggs, err := cb.Build() + if err != nil { + return nil, err + } + + agg.Aggregation.Aggs = append(agg.Aggregation.Aggs, childAggs...) + } + + aggs = append(aggs, agg) + } + + return aggs, nil +} + +func (b *aggBuilderImpl) Histogram(key, field string, fn func(a *HistogramAgg, b AggBuilder)) AggBuilder { + innerAgg := &HistogramAgg{ + Field: field, + } + aggDef := newAggDef(key, &aggContainer{ + Type: "histogram", + Aggregation: innerAgg, + }) + + if fn != nil { + builder := newAggBuilder() + aggDef.builders = append(aggDef.builders, builder) + fn(innerAgg, builder) + } + + b.aggDefs = append(b.aggDefs, aggDef) + + return b +} + +func (b *aggBuilderImpl) DateHistogram(key, field string, fn func(a *DateHistogramAgg, b AggBuilder)) AggBuilder { + innerAgg := &DateHistogramAgg{ + Field: field, + } + aggDef := newAggDef(key, &aggContainer{ + Type: "date_histogram", + Aggregation: innerAgg, + }) + + if fn != nil { + builder := newAggBuilder() + aggDef.builders = append(aggDef.builders, builder) + fn(innerAgg, builder) + } + + b.aggDefs = append(b.aggDefs, aggDef) + + return b +} + +func (b *aggBuilderImpl) Terms(key, field string, fn func(a *TermsAggregation, b AggBuilder)) AggBuilder { + innerAgg := &TermsAggregation{ + Field: field, + Order: make(map[string]any), + } + aggDef := newAggDef(key, &aggContainer{ + Type: "terms", + Aggregation: innerAgg, + }) + + if fn != nil { + builder := newAggBuilder() + aggDef.builders = append(aggDef.builders, builder) + fn(innerAgg, builder) + } + + if len(innerAgg.Order) > 0 { + if orderBy, exists := innerAgg.Order[termsOrderTerm]; exists { + innerAgg.Order["_key"] = orderBy + delete(innerAgg.Order, termsOrderTerm) + } + } + + b.aggDefs = append(b.aggDefs, aggDef) + + return b +} + +func (b *aggBuilderImpl) Nested(key, field string, fn func(a *NestedAggregation, b AggBuilder)) AggBuilder { + innerAgg := &NestedAggregation{ + Path: field, + } + aggDef := newAggDef(key, &aggContainer{ + Type: "nested", + Aggregation: innerAgg, + }) + + if fn != nil { + builder := newAggBuilder() + aggDef.builders = append(aggDef.builders, builder) + fn(innerAgg, builder) + } + + b.aggDefs = append(b.aggDefs, aggDef) + + return b +} + +func (b *aggBuilderImpl) Filters(key string, fn func(a *FiltersAggregation, b AggBuilder)) AggBuilder { + innerAgg := &FiltersAggregation{ + Filters: make(map[string]any), + } + aggDef := newAggDef(key, &aggContainer{ + Type: "filters", + Aggregation: innerAgg, + }) + if fn != nil { + builder := newAggBuilder() + aggDef.builders = append(aggDef.builders, builder) + fn(innerAgg, builder) + } + + b.aggDefs = append(b.aggDefs, aggDef) + + return b +} + +func (b *aggBuilderImpl) GeoHashGrid(key, field string, fn func(a *GeoHashGridAggregation, b AggBuilder)) AggBuilder { + innerAgg := &GeoHashGridAggregation{ + Field: field, + Precision: DefaultGeoHashPrecision, + } + aggDef := newAggDef(key, &aggContainer{ + Type: "geohash_grid", + Aggregation: innerAgg, + }) + + if fn != nil { + builder := newAggBuilder() + aggDef.builders = append(aggDef.builders, builder) + fn(innerAgg, builder) + } + + b.aggDefs = append(b.aggDefs, aggDef) + + return b +} + +func (b *aggBuilderImpl) Metric(key, metricType, field string, fn func(a *MetricAggregation)) AggBuilder { + innerAgg := &MetricAggregation{ + Type: metricType, + Field: field, + Settings: make(map[string]any), + } + + aggDef := newAggDef(key, &aggContainer{ + Type: metricType, + Aggregation: innerAgg, + }) + + if fn != nil { + fn(innerAgg) + } + + b.aggDefs = append(b.aggDefs, aggDef) + + return b +} + +func (b *aggBuilderImpl) Pipeline(key, pipelineType string, bucketPath any, fn func(a *PipelineAggregation)) AggBuilder { + innerAgg := &PipelineAggregation{ + BucketPath: bucketPath, + Settings: make(map[string]any), + } + aggDef := newAggDef(key, &aggContainer{ + Type: pipelineType, + Aggregation: innerAgg, + }) + + if fn != nil { + fn(innerAgg) + } + + b.aggDefs = append(b.aggDefs, aggDef) + + return b +} diff --git a/pkg/tsdb/elasticsearch/client/client.go b/pkg/tsdb/elasticsearch/client/client.go index 9b5cf6dc54b..fbb3e09f092 100644 --- a/pkg/tsdb/elasticsearch/client/client.go +++ b/pkg/tsdb/elasticsearch/client/client.go @@ -1,16 +1,11 @@ package es import ( - "bytes" "context" - "encoding/json" "errors" "fmt" "io" "net/http" - "net/url" - "path" - "strconv" "strings" "time" @@ -73,6 +68,9 @@ var NewClient = func(ctx context.Context, ds *DatasourceInfo, logger log.Logger) ds: ds, configuredFields: ds.ConfiguredFields, indexPattern: ip, + transport: newHTTPTransport(ctx, ds.HTTPClient, ds.URL, logger), + encoder: newRequestEncoder(logger), + parser: newResponseParser(logger), }, nil } @@ -82,6 +80,9 @@ type baseClientImpl struct { configuredFields ConfiguredFields indexPattern IndexPattern logger log.Logger + transport *httpTransport + encoder *requestEncoder + parser *responseParser } func (c *baseClientImpl) GetConfiguredFields() ConfiguredFields { @@ -95,69 +96,11 @@ type multiRequest struct { } func (c *baseClientImpl) executeBatchRequest(uriPath, uriQuery string, requests []*multiRequest) (*http.Response, error) { - bytes, err := c.encodeBatchRequests(requests) + payload, err := c.encoder.encodeBatchRequests(requests) if err != nil { return nil, err } - return c.executeRequest(http.MethodPost, uriPath, uriQuery, bytes) -} - -func (c *baseClientImpl) encodeBatchRequests(requests []*multiRequest) ([]byte, error) { - start := time.Now() - - payload := bytes.Buffer{} - for _, r := range requests { - reqHeader, err := json.Marshal(r.header) - if err != nil { - return nil, err - } - payload.WriteString(string(reqHeader) + "\n") - - reqBody, err := json.Marshal(r.body) - if err != nil { - return nil, err - } - - body := string(reqBody) - body = strings.ReplaceAll(body, "$__interval_ms", strconv.FormatInt(r.interval.Milliseconds(), 10)) - body = strings.ReplaceAll(body, "$__interval", r.interval.String()) - - payload.WriteString(body + "\n") - } - - elapsed := time.Since(start) - c.logger.Debug("Completed encoding of batch requests to json", "duration", elapsed) - - return payload.Bytes(), nil -} - -func (c *baseClientImpl) executeRequest(method, uriPath, uriQuery string, body []byte) (*http.Response, error) { - c.logger.Debug("Sending request to Elasticsearch", "url", c.ds.URL) - u, err := url.Parse(c.ds.URL) - if err != nil { - return nil, backend.DownstreamError(fmt.Errorf("URL could not be parsed: %w", err)) - } - u.Path = path.Join(u.Path, uriPath) - u.RawQuery = uriQuery - - var req *http.Request - if method == http.MethodPost { - req, err = http.NewRequestWithContext(c.ctx, http.MethodPost, u.String(), bytes.NewBuffer(body)) - } else { - req, err = http.NewRequestWithContext(c.ctx, http.MethodGet, u.String(), nil) - } - if err != nil { - return nil, err - } - - req.Header.Set("Content-Type", "application/x-ndjson") - - //nolint:bodyclose - resp, err := c.ds.HTTPClient.Do(req) - if err != nil { - return nil, err - } - return resp, nil + return c.transport.executeBatchRequest(uriPath, uriQuery, payload) } func (c *baseClientImpl) ExecuteMultisearch(r *MultiSearchRequest) (*MultiSearchResponse, error) { @@ -207,7 +150,6 @@ func (c *baseClientImpl) ExecuteMultisearch(r *MultiSearchRequest) (*MultiSearch c.logger.Info("Response received from Elasticsearch", "status", "ok", "statusCode", res.StatusCode, "contentLength", res.ContentLength, "duration", time.Since(start), "stage", StageDatabaseRequest) - start = time.Now() _, resSpan := tracing.DefaultTracer().Start(c.ctx, "datasource.elasticsearch.queryData.executeMultisearch.decodeResponse") defer func() { if err != nil { @@ -217,239 +159,15 @@ func (c *baseClientImpl) ExecuteMultisearch(r *MultiSearchRequest) (*MultiSearch resSpan.End() }() - var msr MultiSearchResponse improvedParsingEnabled := isFeatureEnabled(c.ctx, featuremgmt.FlagElasticsearchImprovedParsing) - if improvedParsingEnabled { - err = StreamMultiSearchResponse(res.Body, &msr) - } else { - dec := json.NewDecoder(res.Body) - err = dec.Decode(&msr) - if err != nil { - // Invalid JSON response from Elasticsearch - err = backend.DownstreamError(err) - } - } + msr, err := c.parser.parseMultiSearchResponse(res.Body, improvedParsingEnabled) if err != nil { - c.logger.Error("Failed to decode response from Elasticsearch", "error", err, "duration", time.Since(start), "improvedParsingEnabled", improvedParsingEnabled) return nil, err } - c.logger.Debug("Completed decoding of response from Elasticsearch", "duration", time.Since(start), "improvedParsingEnabled", improvedParsingEnabled) - msr.Status = res.StatusCode - return &msr, nil -} - -// StreamMultiSearchResponse processes the JSON response in a streaming fashion -func StreamMultiSearchResponse(body io.Reader, msr *MultiSearchResponse) error { - dec := json.NewDecoder(body) - - _, err := dec.Token() // reads the `{` opening brace - if err != nil { - // Invalid JSON response from Elasticsearch - return backend.DownstreamError(err) - } - - for dec.More() { - tok, err := dec.Token() - if err != nil { - return err - } - - if tok == "responses" { - _, err := dec.Token() // reads the `[` opening bracket for responses array - if err != nil { - return err - } - - for dec.More() { - var sr SearchResponse - - _, err := dec.Token() // reads `{` for each SearchResponse - if err != nil { - return err - } - - for dec.More() { - field, err := dec.Token() - if err != nil { - return err - } - - switch field { - case "hits": - sr.Hits = &SearchResponseHits{} - err := processHits(dec, &sr) - if err != nil { - return err - } - case "aggregations": - err := dec.Decode(&sr.Aggregations) - if err != nil { - return err - } - case "error": - err := dec.Decode(&sr.Error) - if err != nil { - return err - } - default: - // skip over unknown fields - err := skipUnknownField(dec) - if err != nil { - return err - } - } - } - - msr.Responses = append(msr.Responses, &sr) - - _, err = dec.Token() // reads `}` closing for each SearchResponse - if err != nil { - return err - } - } - - _, err = dec.Token() // reads the `]` closing bracket for responses array - if err != nil { - return err - } - } else { - err := skipUnknownField(dec) - if err != nil { - return err - } - } - } - - _, err = dec.Token() // reads the `}` closing brace for the entire JSON - return err -} - -// processHits processes the hits in the JSON response incrementally. -func processHits(dec *json.Decoder, sr *SearchResponse) error { - tok, err := dec.Token() // reads the `{` opening brace for the hits object - if err != nil { - return err - } - - if tok != json.Delim('{') { - return fmt.Errorf("expected '{' for hits object, got %v", tok) - } - - for dec.More() { - tok, err := dec.Token() - if err != nil { - return err - } - - switch tok { - case "hits": - if err := streamHitsArray(dec, sr); err != nil { - return err - } - case "total": - var total *SearchResponseHitsTotal - err := dec.Decode(&total) - if err != nil { - // It's possible that the user is using an older version of Elasticsearch (or one that doesn't return what is expected) - // Attempt to parse the total value as an integer in this case - totalInt := 0 - err = dec.Decode(&totalInt) - if err == nil { - total = &SearchResponseHitsTotal{ - Value: totalInt, - } - } else { - // Log the error but do not fail the query - backend.Logger.Debug("failed to decode total hits", "error", err) - } - } - sr.Hits.Total = total - default: - // ignore these fields as they are not used in the current implementation - err := skipUnknownField(dec) - if err != nil { - return err - } - } - } - - // read the closing `}` for the hits object - _, err = dec.Token() - if err != nil { - return err - } - - return nil -} - -// streamHitsArray processes the hits array field incrementally. -func streamHitsArray(dec *json.Decoder, sr *SearchResponse) error { - tok, err := dec.Token() - if err != nil { - return err - } - - // read the opening `[` for the hits array - if tok != json.Delim('[') { - return fmt.Errorf("expected '[' for hits array, got %v", tok) - } - - for dec.More() { - var hit map[string]interface{} - err = dec.Decode(&hit) - if err != nil { - return err - } - - sr.Hits.Hits = append(sr.Hits.Hits, hit) - } - - // read the closing bracket `]` for the hits array - tok, err = dec.Token() - if err != nil { - return err - } - - if tok != json.Delim(']') { - return fmt.Errorf("expected ']' for closing hits array, got %v", tok) - } - - return nil -} - -// skipUnknownField skips over an unknown JSON field's value in the stream. -func skipUnknownField(dec *json.Decoder) error { - tok, err := dec.Token() - if err != nil { - return err - } - - switch tok { - case json.Delim('{'): - // skip everything inside the object until we reach the closing `}` - for dec.More() { - if err := skipUnknownField(dec); err != nil { - return err - } - } - _, err = dec.Token() // read the closing `}` - return err - case json.Delim('['): - // skip everything inside the array until we reach the closing `]` - for dec.More() { - if err := skipUnknownField(dec); err != nil { - return err - } - } - _, err = dec.Token() // read the closing `]` - return err - default: - // no further action needed for primitives - return nil - } + return msr, nil } func (c *baseClientImpl) createMultiSearchRequests(searchRequests []*SearchRequest) ([]*multiRequest, error) { @@ -495,3 +213,10 @@ func (c *baseClientImpl) MultiSearch() *MultiSearchRequestBuilder { func isFeatureEnabled(ctx context.Context, feature string) bool { return backend.GrafanaConfigFromContext(ctx).FeatureToggles().IsEnabled(feature) } + +// StreamMultiSearchResponse processes the JSON response in a streaming fashion +// This is a public wrapper for backward compatibility +func StreamMultiSearchResponse(body io.Reader, msr *MultiSearchResponse) error { + parser := newResponseParser(log.NewNullLogger()) + return parser.streamMultiSearchResponse(body, msr) +} diff --git a/pkg/tsdb/elasticsearch/client/http_transport.go b/pkg/tsdb/elasticsearch/client/http_transport.go new file mode 100644 index 00000000000..9ce33fed6c7 --- /dev/null +++ b/pkg/tsdb/elasticsearch/client/http_transport.go @@ -0,0 +1,66 @@ +package es + +import ( + "bytes" + "context" + "fmt" + "net/http" + "net/url" + "path" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" +) + +// httpTransport handles HTTP communication with Elasticsearch +type httpTransport struct { + ctx context.Context + httpClient *http.Client + baseURL string + logger log.Logger +} + +// newHTTPTransport creates a new HTTP transport +func newHTTPTransport(ctx context.Context, httpClient *http.Client, baseURL string, logger log.Logger) *httpTransport { + return &httpTransport{ + ctx: ctx, + httpClient: httpClient, + baseURL: baseURL, + logger: logger, + } +} + +// executeBatchRequest executes a batch request to Elasticsearch +func (t *httpTransport) executeBatchRequest(uriPath, uriQuery string, body []byte) (*http.Response, error) { + return t.executeRequest(http.MethodPost, uriPath, uriQuery, body) +} + +// executeRequest executes an HTTP request to Elasticsearch +func (t *httpTransport) executeRequest(method, uriPath, uriQuery string, body []byte) (*http.Response, error) { + t.logger.Debug("Sending request to Elasticsearch", "url", t.baseURL) + u, err := url.Parse(t.baseURL) + if err != nil { + return nil, backend.DownstreamError(fmt.Errorf("URL could not be parsed: %w", err)) + } + u.Path = path.Join(u.Path, uriPath) + u.RawQuery = uriQuery + + var req *http.Request + if method == http.MethodPost { + req, err = http.NewRequestWithContext(t.ctx, http.MethodPost, u.String(), bytes.NewBuffer(body)) + } else { + req, err = http.NewRequestWithContext(t.ctx, http.MethodGet, u.String(), nil) + } + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/x-ndjson") + + //nolint:bodyclose + resp, err := t.httpClient.Do(req) + if err != nil { + return nil, err + } + return resp, nil +} diff --git a/pkg/tsdb/elasticsearch/client/query_builder.go b/pkg/tsdb/elasticsearch/client/query_builder.go new file mode 100644 index 00000000000..ed1d16a306d --- /dev/null +++ b/pkg/tsdb/elasticsearch/client/query_builder.go @@ -0,0 +1,112 @@ +package es + +import ( + "strings" +) + +// QueryBuilder represents a query builder +type QueryBuilder struct { + boolQueryBuilder *BoolQueryBuilder +} + +// NewQueryBuilder create a new query builder +func NewQueryBuilder() *QueryBuilder { + return &QueryBuilder{} +} + +// Build builds and return a query builder +func (b *QueryBuilder) Build() (*Query, error) { + q := Query{} + + if b.boolQueryBuilder != nil { + b, err := b.boolQueryBuilder.Build() + if err != nil { + return nil, err + } + q.Bool = b + } + + return &q, nil +} + +// Bool creates and return a query builder +func (b *QueryBuilder) Bool() *BoolQueryBuilder { + if b.boolQueryBuilder == nil { + b.boolQueryBuilder = NewBoolQueryBuilder() + } + return b.boolQueryBuilder +} + +// BoolQueryBuilder represents a bool query builder +type BoolQueryBuilder struct { + filterQueryBuilder *FilterQueryBuilder +} + +// NewBoolQueryBuilder create a new bool query builder +func NewBoolQueryBuilder() *BoolQueryBuilder { + return &BoolQueryBuilder{} +} + +// Filter creates and return a filter query builder +func (b *BoolQueryBuilder) Filter() *FilterQueryBuilder { + if b.filterQueryBuilder == nil { + b.filterQueryBuilder = NewFilterQueryBuilder() + } + return b.filterQueryBuilder +} + +// Build builds and return a bool query builder +func (b *BoolQueryBuilder) Build() (*BoolQuery, error) { + boolQuery := BoolQuery{} + + if b.filterQueryBuilder != nil { + filters, err := b.filterQueryBuilder.Build() + if err != nil { + return nil, err + } + boolQuery.Filters = filters + } + + return &boolQuery, nil +} + +// FilterQueryBuilder represents a filter query builder +type FilterQueryBuilder struct { + filters []Filter +} + +// NewFilterQueryBuilder creates a new filter query builder +func NewFilterQueryBuilder() *FilterQueryBuilder { + return &FilterQueryBuilder{ + filters: make([]Filter, 0), + } +} + +// Build builds and return a filter query builder +func (b *FilterQueryBuilder) Build() ([]Filter, error) { + return b.filters, nil +} + +// AddDateRangeFilter adds a new time range filter +func (b *FilterQueryBuilder) AddDateRangeFilter(timeField string, lte, gte int64, format string) *FilterQueryBuilder { + b.filters = append(b.filters, &RangeFilter{ + Key: timeField, + Lte: lte, + Gte: gte, + Format: format, + }) + return b +} + +// AddQueryStringFilter adds a new query string filter +func (b *FilterQueryBuilder) AddQueryStringFilter(querystring string, analyseWildcard bool) *FilterQueryBuilder { + if len(strings.TrimSpace(querystring)) == 0 { + return b + } + + b.filters = append(b.filters, &QueryStringFilter{ + Query: querystring, + AnalyzeWildcard: analyseWildcard, + }) + return b +} diff --git a/pkg/tsdb/elasticsearch/client/request_encoder.go b/pkg/tsdb/elasticsearch/client/request_encoder.go new file mode 100644 index 00000000000..ae22c8e2694 --- /dev/null +++ b/pkg/tsdb/elasticsearch/client/request_encoder.go @@ -0,0 +1,53 @@ +package es + +import ( + "bytes" + "encoding/json" + "strconv" + "strings" + "time" + + "github.com/grafana/grafana-plugin-sdk-go/backend/log" +) + +// requestEncoder handles encoding of search requests to Elasticsearch format +type requestEncoder struct { + logger log.Logger +} + +// newRequestEncoder creates a new request encoder +func newRequestEncoder(logger log.Logger) *requestEncoder { + return &requestEncoder{ + logger: logger, + } +} + +// encodeBatchRequests encodes multiple requests into NDJSON format +func (e *requestEncoder) encodeBatchRequests(requests []*multiRequest) ([]byte, error) { + start := time.Now() + + payload := bytes.Buffer{} + for _, r := range requests { + reqHeader, err := json.Marshal(r.header) + if err != nil { + return nil, err + } + payload.WriteString(string(reqHeader) + "\n") + + reqBody, err := json.Marshal(r.body) + if err != nil { + return nil, err + } + + body := string(reqBody) + body = strings.ReplaceAll(body, "$__interval_ms", strconv.FormatInt(r.interval.Milliseconds(), 10)) + body = strings.ReplaceAll(body, "$__interval", r.interval.String()) + + payload.WriteString(body + "\n") + } + + elapsed := time.Since(start) + e.logger.Debug("Completed encoding of batch requests to json", "duration", elapsed) + + return payload.Bytes(), nil +} diff --git a/pkg/tsdb/elasticsearch/client/response_parser.go b/pkg/tsdb/elasticsearch/client/response_parser.go new file mode 100644 index 00000000000..a54b238c87a --- /dev/null +++ b/pkg/tsdb/elasticsearch/client/response_parser.go @@ -0,0 +1,262 @@ +package es + +import ( + "encoding/json" + "fmt" + "io" + "time" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" +) + +// responseParser handles parsing of Elasticsearch responses +type responseParser struct { + logger log.Logger +} + +// newResponseParser creates a new response parser +func newResponseParser(logger log.Logger) *responseParser { + return &responseParser{ + logger: logger, + } +} + +// parseMultiSearchResponse parses a multi-search response using streaming +func (p *responseParser) parseMultiSearchResponse(body io.Reader, improvedParsingEnabled bool) (*MultiSearchResponse, error) { + start := time.Now() + + var msr MultiSearchResponse + var err error + + if improvedParsingEnabled { + err = p.streamMultiSearchResponse(body, &msr) + } else { + dec := json.NewDecoder(body) + err = dec.Decode(&msr) + if err != nil { + // Invalid JSON response from Elasticsearch + err = backend.DownstreamError(err) + } + } + + if err != nil { + p.logger.Error("Failed to decode response from Elasticsearch", "error", err, "duration", time.Since(start), "improvedParsingEnabled", improvedParsingEnabled) + return nil, err + } + + p.logger.Debug("Completed decoding of response from Elasticsearch", "duration", time.Since(start), "improvedParsingEnabled", improvedParsingEnabled) + + return &msr, nil +} + +// streamMultiSearchResponse processes the JSON response in a streaming fashion +func (p *responseParser) streamMultiSearchResponse(body io.Reader, msr *MultiSearchResponse) error { + dec := json.NewDecoder(body) + + _, err := dec.Token() // reads the `{` opening brace + if err != nil { + // Invalid JSON response from Elasticsearch + return backend.DownstreamError(err) + } + + for dec.More() { + tok, err := dec.Token() + if err != nil { + return err + } + + if tok == "responses" { + _, err := dec.Token() // reads the `[` opening bracket for responses array + if err != nil { + return err + } + + for dec.More() { + var sr SearchResponse + + _, err := dec.Token() // reads `{` for each SearchResponse + if err != nil { + return err + } + + for dec.More() { + field, err := dec.Token() + if err != nil { + return err + } + + switch field { + case "hits": + sr.Hits = &SearchResponseHits{} + err := p.processHits(dec, &sr) + if err != nil { + return err + } + case "aggregations": + err := dec.Decode(&sr.Aggregations) + if err != nil { + return err + } + case "error": + err := dec.Decode(&sr.Error) + if err != nil { + return err + } + default: + // skip over unknown fields + err := skipUnknownField(dec) + if err != nil { + return err + } + } + } + + msr.Responses = append(msr.Responses, &sr) + + _, err = dec.Token() // reads `}` closing for each SearchResponse + if err != nil { + return err + } + } + + _, err = dec.Token() // reads the `]` closing bracket for responses array + if err != nil { + return err + } + } else { + err := skipUnknownField(dec) + if err != nil { + return err + } + } + } + + _, err = dec.Token() // reads the `}` closing brace for the entire JSON + return err +} + +// processHits processes the hits in the JSON response incrementally. +func (p *responseParser) processHits(dec *json.Decoder, sr *SearchResponse) error { + tok, err := dec.Token() // reads the `{` opening brace for the hits object + if err != nil { + return err + } + + if tok != json.Delim('{') { + return fmt.Errorf("expected '{' for hits object, got %v", tok) + } + + for dec.More() { + tok, err := dec.Token() + if err != nil { + return err + } + + switch tok { + case "hits": + if err := streamHitsArray(dec, sr); err != nil { + return err + } + case "total": + var total *SearchResponseHitsTotal + err := dec.Decode(&total) + if err != nil { + // It's possible that the user is using an older version of Elasticsearch (or one that doesn't return what is expected) + // Attempt to parse the total value as an integer in this case + totalInt := 0 + err = dec.Decode(&totalInt) + if err == nil { + total = &SearchResponseHitsTotal{ + Value: totalInt, + } + } else { + // Log the error but do not fail the query + backend.Logger.Debug("failed to decode total hits", "error", err) + } + } + sr.Hits.Total = total + default: + // ignore these fields as they are not used in the current implementation + err := skipUnknownField(dec) + if err != nil { + return err + } + } + } + + // read the closing `}` for the hits object + _, err = dec.Token() + if err != nil { + return err + } + + return nil +} + +// streamHitsArray processes the hits array field incrementally. +func streamHitsArray(dec *json.Decoder, sr *SearchResponse) error { + tok, err := dec.Token() + if err != nil { + return err + } + + // read the opening `[` for the hits array + if tok != json.Delim('[') { + return fmt.Errorf("expected '[' for hits array, got %v", tok) + } + + for dec.More() { + var hit map[string]interface{} + err = dec.Decode(&hit) + if err != nil { + return err + } + + sr.Hits.Hits = append(sr.Hits.Hits, hit) + } + + // read the closing bracket `]` for the hits array + tok, err = dec.Token() + if err != nil { + return err + } + + if tok != json.Delim(']') { + return fmt.Errorf("expected ']' for closing hits array, got %v", tok) + } + + return nil +} + +// skipUnknownField skips over an unknown JSON field's value in the stream. +func skipUnknownField(dec *json.Decoder) error { + tok, err := dec.Token() + if err != nil { + return err + } + + switch tok { + case json.Delim('{'): + // skip everything inside the object until we reach the closing `}` + for dec.More() { + if err := skipUnknownField(dec); err != nil { + return err + } + } + _, err = dec.Token() // read the closing `}` + return err + case json.Delim('['): + // skip everything inside the array until we reach the closing `]` + for dec.More() { + if err := skipUnknownField(dec); err != nil { + return err + } + } + _, err = dec.Token() // read the closing `]` + return err + default: + // no further action needed for primitives + return nil + } +} diff --git a/pkg/tsdb/elasticsearch/client/search_request.go b/pkg/tsdb/elasticsearch/client/search_request.go index cb0cf004c11..f898517ab07 100644 --- a/pkg/tsdb/elasticsearch/client/search_request.go +++ b/pkg/tsdb/elasticsearch/client/search_request.go @@ -1,7 +1,6 @@ package es import ( - "strings" "time" "github.com/grafana/grafana-plugin-sdk-go/backend" @@ -11,9 +10,6 @@ const ( HighlightPreTagsString = "@HIGHLIGHT@" HighlightPostTagsString = "@/HIGHLIGHT@" HighlightFragmentSize = 2147483647 - DefaultGeoHashPrecision = 3 - - termsOrderTerm = "_term" ) type SortOrder string @@ -192,326 +188,3 @@ func (m *MultiSearchRequestBuilder) Build() (*MultiSearchRequest, error) { Requests: requests, }, nil } - -// QueryBuilder represents a query builder -type QueryBuilder struct { - boolQueryBuilder *BoolQueryBuilder -} - -// NewQueryBuilder create a new query builder -func NewQueryBuilder() *QueryBuilder { - return &QueryBuilder{} -} - -// Build builds and return a query builder -func (b *QueryBuilder) Build() (*Query, error) { - q := Query{} - - if b.boolQueryBuilder != nil { - b, err := b.boolQueryBuilder.Build() - if err != nil { - return nil, err - } - q.Bool = b - } - - return &q, nil -} - -// Bool creates and return a query builder -func (b *QueryBuilder) Bool() *BoolQueryBuilder { - if b.boolQueryBuilder == nil { - b.boolQueryBuilder = NewBoolQueryBuilder() - } - return b.boolQueryBuilder -} - -// BoolQueryBuilder represents a bool query builder -type BoolQueryBuilder struct { - filterQueryBuilder *FilterQueryBuilder -} - -// NewBoolQueryBuilder create a new bool query builder -func NewBoolQueryBuilder() *BoolQueryBuilder { - return &BoolQueryBuilder{} -} - -// Filter creates and return a filter query builder -func (b *BoolQueryBuilder) Filter() *FilterQueryBuilder { - if b.filterQueryBuilder == nil { - b.filterQueryBuilder = NewFilterQueryBuilder() - } - return b.filterQueryBuilder -} - -// Build builds and return a bool query builder -func (b *BoolQueryBuilder) Build() (*BoolQuery, error) { - boolQuery := BoolQuery{} - - if b.filterQueryBuilder != nil { - filters, err := b.filterQueryBuilder.Build() - if err != nil { - return nil, err - } - boolQuery.Filters = filters - } - - return &boolQuery, nil -} - -// FilterQueryBuilder represents a filter query builder -type FilterQueryBuilder struct { - filters []Filter -} - -// NewFilterQueryBuilder creates a new filter query builder -func NewFilterQueryBuilder() *FilterQueryBuilder { - return &FilterQueryBuilder{ - filters: make([]Filter, 0), - } -} - -// Build builds and return a filter query builder -func (b *FilterQueryBuilder) Build() ([]Filter, error) { - return b.filters, nil -} - -// AddDateRangeFilter adds a new time range filter -func (b *FilterQueryBuilder) AddDateRangeFilter(timeField string, lte, gte int64, format string) *FilterQueryBuilder { - b.filters = append(b.filters, &RangeFilter{ - Key: timeField, - Lte: lte, - Gte: gte, - Format: format, - }) - return b -} - -// AddQueryStringFilter adds a new query string filter -func (b *FilterQueryBuilder) AddQueryStringFilter(querystring string, analyseWildcard bool) *FilterQueryBuilder { - if len(strings.TrimSpace(querystring)) == 0 { - return b - } - - b.filters = append(b.filters, &QueryStringFilter{ - Query: querystring, - AnalyzeWildcard: analyseWildcard, - }) - return b -} - -// AggBuilder represents an aggregation builder -type AggBuilder interface { - Histogram(key, field string, fn func(a *HistogramAgg, b AggBuilder)) AggBuilder - DateHistogram(key, field string, fn func(a *DateHistogramAgg, b AggBuilder)) AggBuilder - Terms(key, field string, fn func(a *TermsAggregation, b AggBuilder)) AggBuilder - Nested(key, path string, fn func(a *NestedAggregation, b AggBuilder)) AggBuilder - Filters(key string, fn func(a *FiltersAggregation, b AggBuilder)) AggBuilder - GeoHashGrid(key, field string, fn func(a *GeoHashGridAggregation, b AggBuilder)) AggBuilder - Metric(key, metricType, field string, fn func(a *MetricAggregation)) AggBuilder - Pipeline(key, pipelineType string, bucketPath any, fn func(a *PipelineAggregation)) AggBuilder - Build() (AggArray, error) -} - -type aggBuilderImpl struct { - AggBuilder - aggDefs []*aggDef -} - -func newAggBuilder() *aggBuilderImpl { - return &aggBuilderImpl{ - aggDefs: make([]*aggDef, 0), - } -} - -func (b *aggBuilderImpl) Build() (AggArray, error) { - aggs := make(AggArray, 0) - - for _, aggDef := range b.aggDefs { - agg := &Agg{ - Key: aggDef.key, - Aggregation: aggDef.aggregation, - } - - for _, cb := range aggDef.builders { - childAggs, err := cb.Build() - if err != nil { - return nil, err - } - - agg.Aggregation.Aggs = append(agg.Aggregation.Aggs, childAggs...) - } - - aggs = append(aggs, agg) - } - - return aggs, nil -} - -func (b *aggBuilderImpl) Histogram(key, field string, fn func(a *HistogramAgg, b AggBuilder)) AggBuilder { - innerAgg := &HistogramAgg{ - Field: field, - } - aggDef := newAggDef(key, &aggContainer{ - Type: "histogram", - Aggregation: innerAgg, - }) - - if fn != nil { - builder := newAggBuilder() - aggDef.builders = append(aggDef.builders, builder) - fn(innerAgg, builder) - } - - b.aggDefs = append(b.aggDefs, aggDef) - - return b -} - -func (b *aggBuilderImpl) DateHistogram(key, field string, fn func(a *DateHistogramAgg, b AggBuilder)) AggBuilder { - innerAgg := &DateHistogramAgg{ - Field: field, - } - aggDef := newAggDef(key, &aggContainer{ - Type: "date_histogram", - Aggregation: innerAgg, - }) - - if fn != nil { - builder := newAggBuilder() - aggDef.builders = append(aggDef.builders, builder) - fn(innerAgg, builder) - } - - b.aggDefs = append(b.aggDefs, aggDef) - - return b -} - -func (b *aggBuilderImpl) Terms(key, field string, fn func(a *TermsAggregation, b AggBuilder)) AggBuilder { - innerAgg := &TermsAggregation{ - Field: field, - Order: make(map[string]any), - } - aggDef := newAggDef(key, &aggContainer{ - Type: "terms", - Aggregation: innerAgg, - }) - - if fn != nil { - builder := newAggBuilder() - aggDef.builders = append(aggDef.builders, builder) - fn(innerAgg, builder) - } - - if len(innerAgg.Order) > 0 { - if orderBy, exists := innerAgg.Order[termsOrderTerm]; exists { - innerAgg.Order["_key"] = orderBy - delete(innerAgg.Order, termsOrderTerm) - } - } - - b.aggDefs = append(b.aggDefs, aggDef) - - return b -} - -func (b *aggBuilderImpl) Nested(key, field string, fn func(a *NestedAggregation, b AggBuilder)) AggBuilder { - innerAgg := &NestedAggregation{ - Path: field, - } - aggDef := newAggDef(key, &aggContainer{ - Type: "nested", - Aggregation: innerAgg, - }) - - if fn != nil { - builder := newAggBuilder() - aggDef.builders = append(aggDef.builders, builder) - fn(innerAgg, builder) - } - - b.aggDefs = append(b.aggDefs, aggDef) - - return b -} - -func (b *aggBuilderImpl) Filters(key string, fn func(a *FiltersAggregation, b AggBuilder)) AggBuilder { - innerAgg := &FiltersAggregation{ - Filters: make(map[string]any), - } - aggDef := newAggDef(key, &aggContainer{ - Type: "filters", - Aggregation: innerAgg, - }) - if fn != nil { - builder := newAggBuilder() - aggDef.builders = append(aggDef.builders, builder) - fn(innerAgg, builder) - } - - b.aggDefs = append(b.aggDefs, aggDef) - - return b -} - -func (b *aggBuilderImpl) GeoHashGrid(key, field string, fn func(a *GeoHashGridAggregation, b AggBuilder)) AggBuilder { - innerAgg := &GeoHashGridAggregation{ - Field: field, - Precision: DefaultGeoHashPrecision, - } - aggDef := newAggDef(key, &aggContainer{ - Type: "geohash_grid", - Aggregation: innerAgg, - }) - - if fn != nil { - builder := newAggBuilder() - aggDef.builders = append(aggDef.builders, builder) - fn(innerAgg, builder) - } - - b.aggDefs = append(b.aggDefs, aggDef) - - return b -} - -func (b *aggBuilderImpl) Metric(key, metricType, field string, fn func(a *MetricAggregation)) AggBuilder { - innerAgg := &MetricAggregation{ - Type: metricType, - Field: field, - Settings: make(map[string]any), - } - - aggDef := newAggDef(key, &aggContainer{ - Type: metricType, - Aggregation: innerAgg, - }) - - if fn != nil { - fn(innerAgg) - } - - b.aggDefs = append(b.aggDefs, aggDef) - - return b -} - -func (b *aggBuilderImpl) Pipeline(key, pipelineType string, bucketPath any, fn func(a *PipelineAggregation)) AggBuilder { - innerAgg := &PipelineAggregation{ - BucketPath: bucketPath, - Settings: make(map[string]any), - } - aggDef := newAggDef(key, &aggContainer{ - Type: pipelineType, - Aggregation: innerAgg, - }) - - if fn != nil { - fn(innerAgg) - } - - b.aggDefs = append(b.aggDefs, aggDef) - - return b -} From ff97bfc772a95934606b612259f24106a88c91c3 Mon Sep 17 00:00:00 2001 From: Rafael Bortolon Paulovic Date: Thu, 4 Dec 2025 18:40:21 +0100 Subject: [PATCH 06/48] fix(unified): key_path column default (#114859) fix: key_path column default --- pkg/storage/unified/sql/db/migrations/resource_mig.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/storage/unified/sql/db/migrations/resource_mig.go b/pkg/storage/unified/sql/db/migrations/resource_mig.go index c8a6d980104..c4db9b1dbb9 100644 --- a/pkg/storage/unified/sql/db/migrations/resource_mig.go +++ b/pkg/storage/unified/sql/db/migrations/resource_mig.go @@ -186,7 +186,7 @@ func initResourceTables(mg *migrator.Migrator) string { })) mg.AddMigration("Add key_path column to resource_history", migrator.NewAddColumnMigration(resource_history_table, &migrator.Column{ - Name: "key_path", Type: migrator.DB_NVarchar, Length: 2048, Nullable: false, Default: "", IsLatin: true, + Name: "key_path", Type: migrator.DB_NVarchar, Length: 2048, Nullable: false, Default: "''", IsLatin: true, })) resource_events_table := migrator.Table{ From 26c52796f6c08a8b8c2ad028ee1f89b53e5893db Mon Sep 17 00:00:00 2001 From: beejeebus Date: Thu, 4 Dec 2025 18:03:09 +0000 Subject: [PATCH 07/48] Pass the feature flag instead of false to RegisterAPIService Doh. The feature flag was not actually being used to enable the new DS config CRUD APIs. This PR fixes that, hashtag facepalm. --- pkg/registry/apis/datasource/register.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/registry/apis/datasource/register.go b/pkg/registry/apis/datasource/register.go index bec8fdaec5b..92cf07053c7 100644 --- a/pkg/registry/apis/datasource/register.go +++ b/pkg/registry/apis/datasource/register.go @@ -85,7 +85,8 @@ func RegisterAPIService( accessControl, //nolint:staticcheck // not yet migrated to OpenFeature features.IsEnabledGlobally(featuremgmt.FlagDatasourceQueryTypes), - false, + //nolint:staticcheck // not yet migrated to OpenFeature + features.IsEnabledGlobally(featuremgmt.FlagQueryServiceWithConnections), ) if err != nil { return nil, err From 0291f6d1e71715fb020b16e6e4dd9a66d9093136 Mon Sep 17 00:00:00 2001 From: ismail simsek Date: Thu, 4 Dec 2025 20:58:20 +0100 Subject: [PATCH 08/48] Postgresql: Fix variable interpolation logic when the variable has multiple values (#114058) * fix the variable interpolation * add jest config to grafana-sql * fix broken tests * add variable interpolation tests * lint * apply fix only to postresql datasource --- packages/grafana-sql/jest.config.js | 5 + .../datasource/variable-interpolation.test.ts | 169 ++++++++++++++++++ .../datasource.test.ts | 8 +- .../datasource.ts | 24 ++- 4 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 packages/grafana-sql/jest.config.js create mode 100644 packages/grafana-sql/src/datasource/variable-interpolation.test.ts diff --git a/packages/grafana-sql/jest.config.js b/packages/grafana-sql/jest.config.js new file mode 100644 index 00000000000..93995be84c2 --- /dev/null +++ b/packages/grafana-sql/jest.config.js @@ -0,0 +1,5 @@ +const sharedConfig = require('../../jest.config.js'); +module.exports = { + ...sharedConfig, + rootDir: '../../', +}; diff --git a/packages/grafana-sql/src/datasource/variable-interpolation.test.ts b/packages/grafana-sql/src/datasource/variable-interpolation.test.ts new file mode 100644 index 00000000000..606d2b0f6bb --- /dev/null +++ b/packages/grafana-sql/src/datasource/variable-interpolation.test.ts @@ -0,0 +1,169 @@ +import { DataSourceInstanceSettings } from '@grafana/data'; + +import { DB, SQLOptions, SqlQueryModel } from '../types'; +import { makeVariable } from '../utils/testHelpers'; + +import { SqlDatasource } from './SqlDatasource'; + +// Minimal test implementation of SqlDatasource +class TestSqlDatasource extends SqlDatasource { + getDB(): DB { + return {} as DB; + } + + getQueryModel(): SqlQueryModel { + return { + quoteLiteral: (value: string) => `'${value.replace(/'/g, "''")}'`, + } as SqlQueryModel; + } +} + +describe('SqlDatasource - Variable Interpolation', () => { + const instanceSettings = { + jsonData: { + defaultProject: 'testproject', + }, + } as unknown as DataSourceInstanceSettings; + + let ds: TestSqlDatasource; + + beforeEach(() => { + ds = new TestSqlDatasource(instanceSettings); + }); + + describe('Case 1: Multi-value enabled, single value selected', () => { + it('should escape single quotes in string value', () => { + const variable = makeVariable('id1', 'name1', { multi: true }); + // When we apply the general fix for all SQL data sources these should be uncommented + // expect(ds.interpolateVariable('value1', variable)).toEqual('value1'); + // expect(ds.interpolateVariable("O'Brien", variable)).toEqual("O''Brien"); + expect(ds.interpolateVariable('value1', variable)).toEqual(`'value1'`); + expect(ds.interpolateVariable("O'Brien", variable)).toEqual(`'O''Brien'`); + }); + + it('should handle numeric value', () => { + const variable = makeVariable('id1', 'name1', { multi: true }); + expect(ds.interpolateVariable(42 as unknown as string, variable)).toEqual(42); + }); + }); + + describe('Case 2: Multi-value enabled, multiple values selected', () => { + it('should return quoted, comma-separated values', () => { + const variable = makeVariable('id1', 'name1', { multi: true }); + expect(ds.interpolateVariable(['value1', 'value2', 'value3'], variable)).toEqual("'value1','value2','value3'"); + }); + + it('should escape single quotes in array values', () => { + const variable = makeVariable('id1', 'name1', { multi: true }); + expect(ds.interpolateVariable(["O'Brien", 'Smith', "D'Angelo"], variable)).toEqual( + "'O''Brien','Smith','D''Angelo'" + ); + }); + + it('should handle empty array', () => { + const variable = makeVariable('id1', 'name1', { multi: true }); + expect(ds.interpolateVariable([], variable)).toEqual(''); + }); + }); + + describe('Case 3: Include all enabled, single value selected', () => { + it('should escape single quotes in string value', () => { + const variable = makeVariable('id1', 'name1', { includeAll: true }); + // When we apply the general fix for all SQL data sources these should be uncommented + // expect(ds.interpolateVariable('value1', variable)).toEqual('value1'); + // expect(ds.interpolateVariable("O'Brien", variable)).toEqual("O''Brien"); + expect(ds.interpolateVariable('value1', variable)).toEqual(`'value1'`); + expect(ds.interpolateVariable("O'Brien", variable)).toEqual(`'O''Brien'`); + }); + + it('should handle numeric value', () => { + const variable = makeVariable('id1', 'name1', { includeAll: true }); + expect(ds.interpolateVariable(123 as unknown as string, variable)).toEqual(123); + }); + }); + + describe('Case 4: Include all enabled, "All" value selected', () => { + it('should handle All option as array', () => { + const variable = makeVariable('id1', 'name1', { includeAll: true }); + expect(ds.interpolateVariable(['value1', 'value2', 'value3'], variable)).toEqual("'value1','value2','value3'"); + }); + + it('should handle All option with special characters', () => { + const variable = makeVariable('id1', 'name1', { includeAll: true }); + expect(ds.interpolateVariable(["test'1", 'test2', "test'3"], variable)).toEqual("'test''1','test2','test''3'"); + }); + }); + + describe('Case 5: No include all, no multi-value, single value selected', () => { + it('should escape single quotes in string value', () => { + const variable = makeVariable('id1', 'name1', { multi: false, includeAll: false }); + expect(ds.interpolateVariable('value1', variable)).toEqual('value1'); + expect(ds.interpolateVariable("O'Brien", variable)).toEqual("O''Brien"); + }); + + it('should handle numeric value', () => { + const variable = makeVariable('id1', 'name1', { multi: false, includeAll: false }); + expect(ds.interpolateVariable(999 as unknown as string, variable)).toEqual(999); + }); + + it('should handle empty string', () => { + const variable = makeVariable('id1', 'name1', { multi: false, includeAll: false }); + expect(ds.interpolateVariable('', variable)).toEqual(''); + }); + }); + + describe('Case 6: Both include all and multi-value enabled, single value selected', () => { + it('should escape single quotes in string value', () => { + const variable = makeVariable('id1', 'name1', { multi: true, includeAll: true }); + // When we apply the general fix for all SQL data sources these should be uncommented + // expect(ds.interpolateVariable('value1', variable)).toEqual('value1'); + // expect(ds.interpolateVariable("O'Brien", variable)).toEqual("O''Brien"); + expect(ds.interpolateVariable('value1', variable)).toEqual(`'value1'`); + expect(ds.interpolateVariable("O'Brien", variable)).toEqual(`'O''Brien'`); + }); + + it('should handle numeric value', () => { + const variable = makeVariable('id1', 'name1', { multi: true, includeAll: true }); + expect(ds.interpolateVariable(456 as unknown as string, variable)).toEqual(456); + }); + }); + + describe('Case 7: Both include all and multi-value enabled, "All" value selected', () => { + it('should handle All option as array', () => { + const variable = makeVariable('id1', 'name1', { multi: true, includeAll: true }); + expect(ds.interpolateVariable(['value1', 'value2', 'value3'], variable)).toEqual("'value1','value2','value3'"); + }); + + it('should handle All option with mixed values', () => { + const variable = makeVariable('id1', 'name1', { multi: true, includeAll: true }); + expect(ds.interpolateVariable(['alpha', 'beta', 'gamma'], variable)).toEqual("'alpha','beta','gamma'"); + }); + + it('should handle All option with special characters', () => { + const variable = makeVariable('id1', 'name1', { multi: true, includeAll: true }); + expect(ds.interpolateVariable(["it's", "can't", "won't"], variable)).toEqual("'it''s','can''t','won''t'"); + }); + }); + + describe('Case 8: Both include all and multi-value enabled, multiple values selected', () => { + it('should return quoted, comma-separated values', () => { + const variable = makeVariable('id1', 'name1', { multi: true, includeAll: true }); + expect(ds.interpolateVariable(['value1', 'value2'], variable)).toEqual("'value1','value2'"); + }); + + it('should escape single quotes in array values', () => { + const variable = makeVariable('id1', 'name1', { multi: true, includeAll: true }); + expect(ds.interpolateVariable(["O'Brien", "D'Angelo"], variable)).toEqual("'O''Brien','D''Angelo'"); + }); + + it('should handle single item array', () => { + const variable = makeVariable('id1', 'name1', { multi: true, includeAll: true }); + expect(ds.interpolateVariable(['value1'], variable)).toEqual("'value1'"); + }); + + it('should handle array with single quote escaping', () => { + const variable = makeVariable('id1', 'name1', { multi: true, includeAll: true }); + expect(ds.interpolateVariable(['a', "b'c", 'd'], variable)).toEqual("'a','b''c','d'"); + }); + }); +}); diff --git a/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.test.ts b/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.test.ts index 207991a495a..9067ab7f138 100644 --- a/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.test.ts +++ b/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.test.ts @@ -714,7 +714,7 @@ describe('PostgreSQLDatasource', () => { it('should return a quoted value', () => { const { ds, variable } = setupTestContext({}); variable.multi = true; - expect(ds.interpolateVariable('abc', variable)).toEqual("'abc'"); + expect(ds.interpolateVariable('abc', variable)).toEqual('abc'); }); }); @@ -722,8 +722,8 @@ describe('PostgreSQLDatasource', () => { it('should return a quoted value', () => { const { ds, variable } = setupTestContext({}); variable.multi = true; - expect(ds.interpolateVariable("a'bc", variable)).toEqual("'a''bc'"); - expect(ds.interpolateVariable("a'b'c", variable)).toEqual("'a''b''c'"); + expect(ds.interpolateVariable("a'bc", variable)).toEqual("a''bc"); + expect(ds.interpolateVariable("a'b'c", variable)).toEqual("a''b''c"); }); }); @@ -731,7 +731,7 @@ describe('PostgreSQLDatasource', () => { it('should return a quoted value', () => { const { ds, variable } = setupTestContext({}); variable.includeAll = true; - expect(ds.interpolateVariable('abc', variable)).toEqual("'abc'"); + expect(ds.interpolateVariable('abc', variable)).toEqual('abc'); }); }); }); diff --git a/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.ts b/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.ts index 3a79e700fb7..9ea4498db62 100644 --- a/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.ts +++ b/public/app/plugins/datasource/grafana-postgresql-datasource/datasource.ts @@ -1,6 +1,6 @@ import { v4 as uuidv4 } from 'uuid'; -import { DataSourceInstanceSettings, ScopedVars } from '@grafana/data'; +import { DataSourceInstanceSettings, ScopedVars, VariableWithMultiSupport } from '@grafana/data'; import { LanguageDefinition } from '@grafana/plugin-ui'; import { TemplateSrv } from '@grafana/runtime'; import { @@ -31,6 +31,28 @@ export class PostgresDatasource extends SqlDatasource { return new PostgresQueryModel(target, templateSrv, scopedVars); } + interpolateVariable = (value: string | string[] | number, variable: VariableWithMultiSupport) => { + if (typeof value === 'string') { + // For single string values, just escape quotes (don't add outer quotes) + // The quotes are provided by the query template: WHERE x = '$var' + // We only escape internal single quotes: O'Brien -> O''Brien + return String(value).replace(/'/g, "''"); + } + + if (typeof value === 'number') { + return value; + } + + if (Array.isArray(value)) { + // For arrays, quote each value individually and join with comma + // Used in: WHERE x IN ($var) -> WHERE x IN ('val1','val2','val3') + const quotedValues = value.map((v) => this.getQueryModel().quoteLiteral(v)); + return quotedValues.join(','); + } + + return value; + }; + async getVersion(): Promise { const value = await this.runSql<{ version: number }>(getVersion()); const results = value.fields.version?.values; From ff33237052c6b380a6497f6b7bf81b68e6a93100 Mon Sep 17 00:00:00 2001 From: "alerting-team[bot]" <158350966+alerting-team[bot]@users.noreply.github.com> Date: Thu, 4 Dec 2025 20:31:19 +0000 Subject: [PATCH 09/48] Alerting: Update alerting module to de8c2bbf9eba591078e9d9d7c6cbbe4142ef2d0b (#114877) [create-pull-request] automated change Co-authored-by: yuri-tceretian <25988953+yuri-tceretian@users.noreply.github.com> --- apps/advisor/go.mod | 2 +- apps/advisor/go.sum | 4 ++-- apps/alerting/historian/go.mod | 2 +- apps/alerting/historian/go.sum | 4 ++-- apps/iam/go.mod | 2 +- apps/iam/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/advisor/go.mod b/apps/advisor/go.mod index c6b283dd48d..2318df205ce 100644 --- a/apps/advisor/go.mod +++ b/apps/advisor/go.mod @@ -149,7 +149,7 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/google/wire v0.7.0 // indirect - github.com/grafana/alerting v0.0.0-20251202151018-58fa500f3232 // indirect + github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba // indirect github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f // indirect github.com/grafana/dataplane/sdata v0.0.9 // indirect github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect diff --git a/apps/advisor/go.sum b/apps/advisor/go.sum index add4334f691..76208d30349 100644 --- a/apps/advisor/go.sum +++ b/apps/advisor/go.sum @@ -606,8 +606,8 @@ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/grafana/alerting v0.0.0-20251202151018-58fa500f3232 h1:I9l/BxoqxTlPUVx05t8OsqbdP/qwqOeD2E5makeeIz0= -github.com/grafana/alerting v0.0.0-20251202151018-58fa500f3232/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU= +github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba h1:psKWNETD5nGxmFAlqnWsXoRyUwSa2GHNEMSEDKGKfQ4= +github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU= 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/alerting/historian/go.mod b/apps/alerting/historian/go.mod index d14fc9ad55e..37298b0f204 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-20251202151018-58fa500f3232 + github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 github.com/grafana/grafana-app-sdk v0.48.4 github.com/grafana/grafana-app-sdk/logging v0.48.3 diff --git a/apps/alerting/historian/go.sum b/apps/alerting/historian/go.sum index 5420cd9d519..9e44308979b 100644 --- a/apps/alerting/historian/go.sum +++ b/apps/alerting/historian/go.sum @@ -218,8 +218,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-20251202151018-58fa500f3232 h1:I9l/BxoqxTlPUVx05t8OsqbdP/qwqOeD2E5makeeIz0= -github.com/grafana/alerting v0.0.0-20251202151018-58fa500f3232/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU= +github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba h1:psKWNETD5nGxmFAlqnWsXoRyUwSa2GHNEMSEDKGKfQ4= +github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU= 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.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= diff --git a/apps/iam/go.mod b/apps/iam/go.mod index 784b98bba4e..bfe1fa1c50d 100644 --- a/apps/iam/go.mod +++ b/apps/iam/go.mod @@ -221,7 +221,7 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.15.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/grafana/alerting v0.0.0-20251202151018-58fa500f3232 // indirect + github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba // 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/iam/go.sum b/apps/iam/go.sum index a10e7d6be79..4c25bd92d43 100644 --- a/apps/iam/go.sum +++ b/apps/iam/go.sum @@ -817,8 +817,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= 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-20251202151018-58fa500f3232 h1:I9l/BxoqxTlPUVx05t8OsqbdP/qwqOeD2E5makeeIz0= -github.com/grafana/alerting v0.0.0-20251202151018-58fa500f3232/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU= +github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba h1:psKWNETD5nGxmFAlqnWsXoRyUwSa2GHNEMSEDKGKfQ4= +github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU= 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/go.mod b/go.mod index 57cebdcede4..7ef730e391d 100644 --- a/go.mod +++ b/go.mod @@ -87,7 +87,7 @@ require ( github.com/googleapis/gax-go/v2 v2.15.0 // @grafana/grafana-backend-group github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // @grafana/grafana-app-platform-squad - github.com/grafana/alerting v0.0.0-20251202151018-58fa500f3232 // @grafana/alerting-backend + github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba // @grafana/alerting-backend github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f // @grafana/identity-access-team github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 // @grafana/identity-access-team github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics diff --git a/go.sum b/go.sum index 11cc99b720d..7723cfa19a9 100644 --- a/go.sum +++ b/go.sum @@ -1613,8 +1613,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= 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-20251202151018-58fa500f3232 h1:I9l/BxoqxTlPUVx05t8OsqbdP/qwqOeD2E5makeeIz0= -github.com/grafana/alerting v0.0.0-20251202151018-58fa500f3232/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU= +github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba h1:psKWNETD5nGxmFAlqnWsXoRyUwSa2GHNEMSEDKGKfQ4= +github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU= 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= From f07cc211bdfb05f6507eadfd864caae0ddcb410a Mon Sep 17 00:00:00 2001 From: Steve Simpson Date: Thu, 4 Dec 2025 22:07:22 +0100 Subject: [PATCH 10/48] Alerting: Add command line parsing for historian options (#114865) --- apps/alerting/historian/go.mod | 2 +- .../historian/pkg/app/config/config.go | 56 ++++++++++++ .../historian/pkg/app/config/config_test.go | 85 +++++++++++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 apps/alerting/historian/pkg/app/config/config_test.go diff --git a/apps/alerting/historian/go.mod b/apps/alerting/historian/go.mod index 37298b0f204..5963ac8139c 100644 --- a/apps/alerting/historian/go.mod +++ b/apps/alerting/historian/go.mod @@ -9,6 +9,7 @@ require ( github.com/grafana/grafana-app-sdk v0.48.4 github.com/grafana/grafana-app-sdk/logging v0.48.3 github.com/prometheus/client_golang v1.23.2 + github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 go.opentelemetry.io/otel v1.38.0 go.opentelemetry.io/otel/trace v1.38.0 @@ -113,7 +114,6 @@ require ( github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect github.com/spf13/cast v1.10.0 // indirect - github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect github.com/uber/jaeger-lib v2.4.1+incompatible // indirect diff --git a/apps/alerting/historian/pkg/app/config/config.go b/apps/alerting/historian/pkg/app/config/config.go index dffb672b2ef..d294395643e 100644 --- a/apps/alerting/historian/pkg/app/config/config.go +++ b/apps/alerting/historian/pkg/app/config/config.go @@ -1,10 +1,20 @@ package config import ( + "net/url" + "time" + + "github.com/spf13/pflag" + "github.com/grafana/alerting/notify/historian/lokiclient" "github.com/grafana/grafana-app-sdk/simple" ) +const ( + lokiDefaultMaxQueryLength = 721 * time.Hour // 30d1h, matches the default value in Loki + lokiDefaultMaxQuerySize = 65536 // 64kb +) + type NotificationConfig struct { Enabled bool Loki lokiclient.LokiConfig @@ -14,3 +24,49 @@ type RuntimeConfig struct { GetAlertStateHistoryHandler simple.AppCustomRouteHandler Notification NotificationConfig } + +func (n *NotificationConfig) AddFlagsWithPrefix(prefix string, flags *pflag.FlagSet) { + flags.BoolVar(&n.Enabled, prefix+".enabled", false, "Enable notification query endpoints") + addLokiFlags(&n.Loki, prefix+".loki", flags) +} + +func (r *RuntimeConfig) AddFlagsWithPrefix(prefix string, flags *pflag.FlagSet) { + r.Notification.AddFlagsWithPrefix(prefix+".notification", flags) +} + +func (r *RuntimeConfig) AddFlags(flags *pflag.FlagSet) { + r.AddFlagsWithPrefix("alerting.historian", flags) +} + +type urlVar struct { + u **url.URL +} + +// String implements flag.Value +func (v urlVar) String() string { + if v.u == nil || *v.u == nil { + return "" + } + return (*v.u).Redacted() +} + +// Set implements flag.Value +func (v urlVar) Set(s string) error { + u, err := url.Parse(s) + if err != nil { + return err + } + *v.u = u + return nil +} + +// Type implements flag.Value +func (v urlVar) Type() string { + return "url" +} + +func addLokiFlags(l *lokiclient.LokiConfig, prefix string, flags *pflag.FlagSet) { + flags.Var(urlVar{&l.ReadPathURL}, prefix+".read-url", "URL to Loki instance for performing queries") + flags.DurationVar(&l.MaxQueryLength, prefix+".max-query-length", lokiDefaultMaxQueryLength, "Maximum allowed time range for queries") + flags.IntVar(&l.MaxQuerySize, prefix+".max-query-size", lokiDefaultMaxQuerySize, "Maximum allowed size of a query string passed to Loki") +} diff --git a/apps/alerting/historian/pkg/app/config/config_test.go b/apps/alerting/historian/pkg/app/config/config_test.go new file mode 100644 index 00000000000..870ed8e9785 --- /dev/null +++ b/apps/alerting/historian/pkg/app/config/config_test.go @@ -0,0 +1,85 @@ +package config + +import ( + "net/url" + "testing" + "time" + + "github.com/grafana/alerting/notify/historian/lokiclient" + "github.com/spf13/pflag" + "github.com/stretchr/testify/require" +) + +func TestRuntimeConfig(t *testing.T) { + lokiURL := mustParseURL("http://localhost:3100") + + tests := []struct { + name string + args []string + expected RuntimeConfig + }{ + { + name: "default config", + args: []string{}, + expected: RuntimeConfig{ + Notification: NotificationConfig{ + Enabled: false, + Loki: lokiclient.LokiConfig{ + ReadPathURL: nil, + MaxQueryLength: 721 * time.Hour, + MaxQuerySize: 65536, + }, + }, + }, + }, + { + name: "with notification enabled", + args: []string{"--alerting.historian.notification.enabled"}, + expected: RuntimeConfig{ + Notification: NotificationConfig{ + Enabled: true, + Loki: lokiclient.LokiConfig{ + ReadPathURL: nil, + MaxQueryLength: 721 * time.Hour, + MaxQuerySize: 65536, + }, + }, + }, + }, + { + name: "with loki read url", + args: []string{"--alerting.historian.notification.loki.read-url=http://localhost:3100"}, + expected: RuntimeConfig{ + Notification: NotificationConfig{ + Enabled: false, + Loki: lokiclient.LokiConfig{ + ReadPathURL: lokiURL, + MaxQueryLength: 721 * time.Hour, + MaxQuerySize: 65536, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := &RuntimeConfig{} + flags := pflag.NewFlagSet("test", pflag.ContinueOnError) + + cfg.AddFlags(flags) + + err := flags.Parse(tt.args) + require.NoError(t, err) + require.Equal(t, tt.expected, *cfg) + }) + } +} + +func mustParseURL(s string) *url.URL { + u, err := url.Parse(s) + if err != nil { + panic(err) + } + return u +} From 4969df8a8306de6073a9edc584f91e77931eb569 Mon Sep 17 00:00:00 2001 From: Steve Simpson Date: Thu, 4 Dec 2025 22:56:07 +0100 Subject: [PATCH 11/48] Alerting: Add basic auth options to historian args (#114880) --- .../historian/pkg/app/config/config.go | 3 +++ .../historian/pkg/app/config/config_test.go | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/apps/alerting/historian/pkg/app/config/config.go b/apps/alerting/historian/pkg/app/config/config.go index d294395643e..5d8027d933b 100644 --- a/apps/alerting/historian/pkg/app/config/config.go +++ b/apps/alerting/historian/pkg/app/config/config.go @@ -67,6 +67,9 @@ func (v urlVar) Type() string { func addLokiFlags(l *lokiclient.LokiConfig, prefix string, flags *pflag.FlagSet) { flags.Var(urlVar{&l.ReadPathURL}, prefix+".read-url", "URL to Loki instance for performing queries") + flags.StringVar(&l.BasicAuthUser, prefix+".user", "", "Basic auth Username to authenticate to the Loki instance") + flags.StringVar(&l.BasicAuthPassword, prefix+".password", "", "Basic auth password to authenticate to the Loki instance") + flags.StringVar(&l.TenantID, prefix+".tenant-id", "", "Value to use for X-Scope-OrgID") flags.DurationVar(&l.MaxQueryLength, prefix+".max-query-length", lokiDefaultMaxQueryLength, "Maximum allowed time range for queries") flags.IntVar(&l.MaxQuerySize, prefix+".max-query-size", lokiDefaultMaxQuerySize, "Maximum allowed size of a query string passed to Loki") } diff --git a/apps/alerting/historian/pkg/app/config/config_test.go b/apps/alerting/historian/pkg/app/config/config_test.go index 870ed8e9785..8234f4c945f 100644 --- a/apps/alerting/historian/pkg/app/config/config_test.go +++ b/apps/alerting/historian/pkg/app/config/config_test.go @@ -47,15 +47,23 @@ func TestRuntimeConfig(t *testing.T) { }, }, { - name: "with loki read url", - args: []string{"--alerting.historian.notification.loki.read-url=http://localhost:3100"}, + name: "with loki options", + args: []string{ + "--alerting.historian.notification.loki.read-url=http://localhost:3100", + "--alerting.historian.notification.loki.user=foo", + "--alerting.historian.notification.loki.password=bar", + "--alerting.historian.notification.loki.tenant-id=baz", + }, expected: RuntimeConfig{ Notification: NotificationConfig{ Enabled: false, Loki: lokiclient.LokiConfig{ - ReadPathURL: lokiURL, - MaxQueryLength: 721 * time.Hour, - MaxQuerySize: 65536, + ReadPathURL: lokiURL, + BasicAuthUser: "foo", + BasicAuthPassword: "bar", + TenantID: "baz", + MaxQueryLength: 721 * time.Hour, + MaxQuerySize: 65536, }, }, }, From 10ad94af385bd618d0cdce0bf2281da86e4c051a Mon Sep 17 00:00:00 2001 From: Galen Kistler <109082771+gtk-grafana@users.noreply.github.com> Date: Thu, 4 Dec 2025 17:02:54 -0600 Subject: [PATCH 12/48] TableRT: fix bug preventing users from selecting filter operators (#114860) * fix: bug preventing users from selecting filter operators --- .../components/Table/TableRT/FilterList.tsx | 3 ++ .../components/Table/TableRT/FilterPopup.tsx | 32 +++++++++++-------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/grafana-ui/src/components/Table/TableRT/FilterList.tsx b/packages/grafana-ui/src/components/Table/TableRT/FilterList.tsx index 273f97d684f..cb0a8116f9e 100644 --- a/packages/grafana-ui/src/components/Table/TableRT/FilterList.tsx +++ b/packages/grafana-ui/src/components/Table/TableRT/FilterList.tsx @@ -23,6 +23,7 @@ interface Props { setSearchFilter: (value: string) => void; operator: SelectableValue; setOperator: (item: SelectableValue) => void; + referenceElement: HTMLElement; } const ITEM_HEIGHT = 28; @@ -81,6 +82,7 @@ export const FilterList = ({ setSearchFilter, operator, setOperator, + referenceElement, }: Props) => { const regex = useMemo(() => new RegExp(searchFilter, caseSensitive ? undefined : 'i'), [searchFilter, caseSensitive]); const items = useMemo( @@ -186,6 +188,7 @@ export const FilterList = ({ {showOperators && ( getFilteredOptions(options, filterValue), [options, filterValue]); const [values, setValues] = useState(filteredOptions); const [matchCase, setMatchCase] = useState(false); + const ref = useRef(null); - const onCancel = useCallback((event?: React.MouseEvent) => onClose(), [onClose]); + const onCancel = useCallback(() => onClose(), [onClose]); const onFilter = useCallback( (event: React.MouseEvent) => { @@ -70,7 +71,7 @@ export const FilterPopup = ({ {/* This is just blocking click events from bubbeling and should not have a keyboard interaction. */} {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */} -
+
@@ -87,17 +88,20 @@ export const FilterPopup = ({ />
- + {ref.current && ( + + )} From c53500378e248800e77f39c01137b1a681f6cb49 Mon Sep 17 00:00:00 2001 From: "grafana-pr-automation[bot]" <140550294+grafana-pr-automation[bot]@users.noreply.github.com> Date: Fri, 5 Dec 2025 00:48:25 +0000 Subject: [PATCH 13/48] I18n: Download translations from Crowdin (#114886) New Crowdin translations by GitHub Action Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- public/locales/cs-CZ/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/de-DE/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/es-ES/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/fr-FR/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/hu-HU/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/id-ID/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/it-IT/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/ja-JP/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/ko-KR/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/nl-NL/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/pl-PL/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/pt-BR/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/pt-PT/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/ru-RU/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/sv-SE/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/tr-TR/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/zh-Hans/grafana.json | 40 ++++++++++++++++++++++++++--- public/locales/zh-Hant/grafana.json | 40 ++++++++++++++++++++++++++--- 18 files changed, 648 insertions(+), 72 deletions(-) diff --git a/public/locales/cs-CZ/grafana.json b/public/locales/cs-CZ/grafana.json index 01c70cde5bb..99f381a244e 100644 --- a/public/locales/cs-CZ/grafana.json +++ b/public/locales/cs-CZ/grafana.json @@ -5868,14 +5868,26 @@ }, "annotation-settings-edit": { "back-to-list": "Zpět na seznam", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Odstranit", "description-color-annotation-event-markers": "Barva, která bude použita pro značky vysvětlivek události", "description-enabled-annotation-query-issued-every-dashboard": "Je-li povoleno, při každém obnovení nástěnky bude aktivován dotaz na vysvětlivky", - "description-hidden": "Dotazy na vysvětlivky lze zapnout nebo vypnout v horní části nástěnky. Pokud je tato možnost zaškrtnuta, bude tento přepínač skrytý.", + "label-annotation-controls-display": "", "label-color": "Barva", "label-data-source": "Zdroj dat", "label-enabled": "Povoleno", - "label-hidden": "Skryté", "label-name": "Název", "label-show-in": "Zobrazit v", "placeholder-choose-panels": "Vybrat panely", @@ -5931,6 +5943,7 @@ "label-open-link-in-new-tab": "Otevřít odkaz v nové záložce", "label-options": "Možnosti", "label-show-as-dropdown": "Zobrazit jako rozevírací seznam", + "label-show-in-controls-menu": "", "label-title": "Název", "label-tooltip": "Popisek", "label-type": "Typ", @@ -6496,6 +6509,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Formulář editoru proměnné", "back-to-list": "Zpět na seznam", @@ -6810,8 +6842,8 @@ "test": "Test" }, "cloud-info-box": { - "body-alert": "Nebo si to zjednodušte a získejte {{mainDS}} (a {{extraDS}}) – plně spravované, škálovatelné a hostované zdroje dat od Grafana Labs s <6>celoživotním bezplatným plánem Grafana Cloud.", - "title-alert": "Nakonfigurujte zdroje dat {{mainDS}} níže" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Odstranit nástěnku", diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index ff43be3c8e0..f84c77dd97f 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -5824,14 +5824,26 @@ }, "annotation-settings-edit": { "back-to-list": "Zurück zur Liste", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Löschen", "description-color-annotation-event-markers": "Farbe, die für Ereignismarkierungen von Anmerkungen verwendet werden soll", "description-enabled-annotation-query-issued-every-dashboard": "Wenn dies aktiviert ist, wird bei jeder Aktualisierung des Dashboards eine Anmerkungsabfrage ausgegeben", - "description-hidden": "Anmerkungsabfragen können im oberen Bereich des Dashboards ein- oder ausgeschaltet werden. Wenn diese Option aktiviert ist, wird dieser Schalter ausgeblendet.", + "label-annotation-controls-display": "", "label-color": "Farbe", "label-data-source": "Datenquelle", "label-enabled": "Aktiviert", - "label-hidden": "Ausgeblendet", "label-name": "Name", "label-show-in": "Anzeigen in", "placeholder-choose-panels": "Panels auswählen", @@ -5887,6 +5899,7 @@ "label-open-link-in-new-tab": "Link in neuem Tab öffnen", "label-options": "Optionen", "label-show-as-dropdown": "Als Dropdown anzeigen", + "label-show-in-controls-menu": "", "label-title": "Titel", "label-tooltip": "Tooltip", "label-type": "Typ", @@ -6448,6 +6461,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Variablen-Editor-Formular", "back-to-list": "Zurück zur Liste", @@ -6762,8 +6794,8 @@ "test": "Test" }, "cloud-info-box": { - "body-alert": "Oder sparen Sie sich den Aufwand und erhalten Sie {{mainDS}} (und {{extraDS}}) als vollständig verwaltete, skalierbare und gehostete Datenquellen von Grafana Labs – mit dem <6>jederzeit kostenlosen Grafana-Cloud-Plan.", - "title-alert": "Konfigurieren Sie unten Ihre {{mainDS}} Datenquelle" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Dashboard löschen", diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index 710ee0e7739..70b7097bcc7 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -5824,14 +5824,26 @@ }, "annotation-settings-edit": { "back-to-list": "Regresar a la lista", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Eliminar", "description-color-annotation-event-markers": "Color que se utilizará para los marcadores de eventos de anotación", "description-enabled-annotation-query-issued-every-dashboard": "Cuando está habilitada, la consulta de anotación se emite cada vez que se actualiza el dashboard", - "description-hidden": "Las consultas de anotación se pueden activar o desactivar en la parte superior del dashboard. Con esta opción marcada, este conmutador se ocultará.", + "label-annotation-controls-display": "", "label-color": "Color", "label-data-source": "Fuente de datos", "label-enabled": "Activado", - "label-hidden": "Oculto", "label-name": "Nombre", "label-show-in": "Mostrar en", "placeholder-choose-panels": "Elegir paneles", @@ -5887,6 +5899,7 @@ "label-open-link-in-new-tab": "Abrir enlace en pestaña nueva", "label-options": "Opciones", "label-show-as-dropdown": "Mostrar como desplegable", + "label-show-in-controls-menu": "", "label-title": "Título", "label-tooltip": "Descripción emergente", "label-type": "Tipo", @@ -6448,6 +6461,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Formulario del editor de variables", "back-to-list": "Regresar a la lista", @@ -6762,8 +6794,8 @@ "test": "Prueba" }, "cloud-info-box": { - "body-alert": "O ahórrate el esfuerzo y consigue {{mainDS}} (y{{extraDS}}) como fuentes de datos totalmente gestionadas, escalables y alojadas de Grafana Labs con el <6>plan Grafana Cloud gratuito para siempre.", - "title-alert": "Configura tu fuente de datos {{mainDS}} a continuación" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Eliminar panel de control", diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index 601ff0a182d..098050412e8 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -5824,14 +5824,26 @@ }, "annotation-settings-edit": { "back-to-list": "Retour à la liste", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Supprimer", "description-color-annotation-event-markers": "Couleur à utiliser pour les marqueurs d’événement d’annotation", "description-enabled-annotation-query-issued-every-dashboard": "Lorsque cette option est activée, la requête d’annotation est émise à chaque actualisation du tableau de bord", - "description-hidden": "Les requêtes d’annotation peuvent être activées ou désactivées en haut du tableau de bord. Lorsque cette option est cochée, ce bouton sera masqué.", + "label-annotation-controls-display": "", "label-color": "Couleur", "label-data-source": "Source de données", "label-enabled": "Activé", - "label-hidden": "Masqué", "label-name": "Nom", "label-show-in": "Afficher dans", "placeholder-choose-panels": "Choisir des panneaux", @@ -5887,6 +5899,7 @@ "label-open-link-in-new-tab": "Ouvrir le lien dans un nouvel onglet", "label-options": "Options", "label-show-as-dropdown": "Afficher sous forme de menu déroulant", + "label-show-in-controls-menu": "", "label-title": "Titre", "label-tooltip": "Infobulle", "label-type": "Type", @@ -6448,6 +6461,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Formulaire de l’éditeur de variables", "back-to-list": "Retour à la liste", @@ -6762,8 +6794,8 @@ "test": "Test" }, "cloud-info-box": { - "body-alert": "Ou simplifiez-vous la tâche et obtenez {{mainDS}} (et {{extraDS}}) sous forme de sources de données entièrement gérées, évolutives et hébergées par Grafana Labs avec le <6>plan Grafana Cloud gratuit à vie.", - "title-alert": "Configurer la source de données {{mainDS}} ci-dessous" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Supprimer le tableau de bord", diff --git a/public/locales/hu-HU/grafana.json b/public/locales/hu-HU/grafana.json index 62de97b4434..28130cb2ec2 100644 --- a/public/locales/hu-HU/grafana.json +++ b/public/locales/hu-HU/grafana.json @@ -5824,14 +5824,26 @@ }, "annotation-settings-edit": { "back-to-list": "Vissza a listához", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Törlés", "description-color-annotation-event-markers": "A jegyzetesemény-jelölőkhöz használandó szín", "description-enabled-annotation-query-issued-every-dashboard": "Ha engedélyezve van, a jegyzetlekérdezés az irányítópult minden frissítésekor megjelenik", - "description-hidden": "A jegyzetlekérdezések be- és kikapcsolhatók az irányítópult tetején. Ha ez a lehetőség be van jelölve, ez a kapcsoló el lesz rejtve.", + "label-annotation-controls-display": "", "label-color": "Szín", "label-data-source": "Adatforrás", "label-enabled": "Engedélyezve", - "label-hidden": "Rejtett", "label-name": "Név", "label-show-in": "Megjelenítés itt:", "placeholder-choose-panels": "Válassza ki a paneleket", @@ -5887,6 +5899,7 @@ "label-open-link-in-new-tab": "Hivatkozás megnyitása új lapon", "label-options": "Beállítások", "label-show-as-dropdown": "Megjelenítés legördülő menüként", + "label-show-in-controls-menu": "", "label-title": "Cím", "label-tooltip": "Elemleírás", "label-type": "Típus", @@ -6448,6 +6461,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Változószerkesztő űrlap", "back-to-list": "Vissza a listához", @@ -6762,8 +6794,8 @@ "test": "Teszt" }, "cloud-info-box": { - "body-alert": "Vagy hagyja ki a próbálkozást, és szerezze be a(z) {{mainDS}} adatforrást (és {{extraDS}}) a Grafana Labs teljes körűen felügyelt, skálázható és hosztolt adatforrásait az <6>örökre ingyenes Grafana Cloud-előfizetéssel.", - "title-alert": "Konfigurálja {{mainDS}} adatforrását alább" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Irányítópult törlése", diff --git a/public/locales/id-ID/grafana.json b/public/locales/id-ID/grafana.json index 622130e0572..72598112d52 100644 --- a/public/locales/id-ID/grafana.json +++ b/public/locales/id-ID/grafana.json @@ -5802,14 +5802,26 @@ }, "annotation-settings-edit": { "back-to-list": "Kembali ke daftar", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Hapus", "description-color-annotation-event-markers": "Warna yang akan digunakan untuk penanda peristiwa anotasi", "description-enabled-annotation-query-issued-every-dashboard": "Ketika diaktifkan, kueri anotasi dikeluarkan setiap muat ulang dasbor", - "description-hidden": "Kueri anotasi dapat diaktifkan atau dinonaktifkan di bagian atas dasbor. Dengan mencentang opsi ini, tombol ini akan disembunyikan.", + "label-annotation-controls-display": "", "label-color": "Warna", "label-data-source": "Sumber data", "label-enabled": "Aktif", - "label-hidden": "Tersembunyi", "label-name": "Nama", "label-show-in": "Tampilkan di", "placeholder-choose-panels": "Pilih panel", @@ -5865,6 +5877,7 @@ "label-open-link-in-new-tab": "Buka tautan di tab baru", "label-options": "Opsi", "label-show-as-dropdown": "Tampilkan sebagai dropdown", + "label-show-in-controls-menu": "", "label-title": "Judul", "label-tooltip": "Tooltip", "label-type": "Jenis", @@ -6424,6 +6437,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Formulir editor variabel", "back-to-list": "Kembali ke daftar", @@ -6738,8 +6770,8 @@ "test": "Tes" }, "cloud-info-box": { - "body-alert": "Atau lewati proses dan dapatkan {{mainDS}} (dan {{extraDS}}) sebagai sumber data yang dikelola sepenuhnya, dapat diskalakan, dan di-host dari Grafana Labs dengan <6>paket Grafana Cloud gratis selamanya.", - "title-alert": "Konfigurasikan sumber data {{mainDS}} Anda di bawah ini" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Hapus dasbor", diff --git a/public/locales/it-IT/grafana.json b/public/locales/it-IT/grafana.json index fe666a11dd5..5b8c69dff0b 100644 --- a/public/locales/it-IT/grafana.json +++ b/public/locales/it-IT/grafana.json @@ -5824,14 +5824,26 @@ }, "annotation-settings-edit": { "back-to-list": "Torna all'elenco", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Elimina", "description-color-annotation-event-markers": "Colore da utilizzare per gli indicatori degli eventi di annotazione", "description-enabled-annotation-query-issued-every-dashboard": "Se abilitata, la query di annotazione viene emessa a ogni aggiornamento della dashboard", - "description-hidden": "Le query di annotazione possono essere attivate o disattivate nella parte superiore della dashboard. Se questa opzione è selezionata, il toggle verrà nascosto.", + "label-annotation-controls-display": "", "label-color": "Colore", "label-data-source": "Sorgente dati", "label-enabled": "Abilitato", - "label-hidden": "Nascosto", "label-name": "Nome", "label-show-in": "Mostra in", "placeholder-choose-panels": "Scegli i pannelli", @@ -5887,6 +5899,7 @@ "label-open-link-in-new-tab": "Apri link in un nuova scheda", "label-options": "Opzioni", "label-show-as-dropdown": "Mostra come menu a discesa", + "label-show-in-controls-menu": "", "label-title": "Titolo", "label-tooltip": "Suggerimento", "label-type": "Tipo", @@ -6448,6 +6461,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Modulo di modifica delle variabili", "back-to-list": "Torna all'elenco", @@ -6762,8 +6794,8 @@ "test": "Prova" }, "cloud-info-box": { - "body-alert": "Oppure puoi ignorare tutto e utilizzare {{mainDS}} (e {{extraDS}}) come origini dei dati completamente gestite, scalabili e ospitate da Grafana Labs con il <6>piano Grafana Cloud gratuito per sempre.", - "title-alert": "Configura la tua origine dei dati {{mainDS}} qui sotto" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Elimina dashboard", diff --git a/public/locales/ja-JP/grafana.json b/public/locales/ja-JP/grafana.json index 213ddb5a7dd..14c46a83f7f 100644 --- a/public/locales/ja-JP/grafana.json +++ b/public/locales/ja-JP/grafana.json @@ -5802,14 +5802,26 @@ }, "annotation-settings-edit": { "back-to-list": "一覧に戻る", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "削除", "description-color-annotation-event-markers": "注釈イベントマーカーに使用する色", "description-enabled-annotation-query-issued-every-dashboard": "有効にすると、ダッシュボードの更新ごとに注釈クエリが発行されます", - "description-hidden": "ダッシュボード上部で、注釈クエリのオン/オフを切り替えられます。このオプションをオンにすると、このトグルは非表示になります。", + "label-annotation-controls-display": "", "label-color": "色", "label-data-source": "データソース", "label-enabled": "有効化", - "label-hidden": "非表示", "label-name": "名前", "label-show-in": "表示先", "placeholder-choose-panels": "パネルを選択", @@ -5865,6 +5877,7 @@ "label-open-link-in-new-tab": "リンクを新しいタブで開く", "label-options": "オプション", "label-show-as-dropdown": "ドロップダウンリストとして表示", + "label-show-in-controls-menu": "", "label-title": "タイトル", "label-tooltip": "ツールチップ", "label-type": "タイプ", @@ -6424,6 +6437,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "変数エディタフォーム", "back-to-list": "一覧に戻る", @@ -6738,8 +6770,8 @@ "test": "テスト" }, "cloud-info-box": { - "body-alert": "または、<6>永久無料のGrafana Cloudプランで、Grafana Labsからフルマネージドでスケーラブルなホスト済みデータソースの{{mainDS}}(および{{extraDS}})を入手して、手間を省くことも可能です。", - "title-alert": "以下で{{mainDS}}データソースを設定します" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "ダッシュボードを削除する", diff --git a/public/locales/ko-KR/grafana.json b/public/locales/ko-KR/grafana.json index 6841d4ae54d..df4cad85150 100644 --- a/public/locales/ko-KR/grafana.json +++ b/public/locales/ko-KR/grafana.json @@ -5802,14 +5802,26 @@ }, "annotation-settings-edit": { "back-to-list": "목록으로 돌아가기", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "삭제", "description-color-annotation-event-markers": "주석 이벤트 마커에 사용할 색상", "description-enabled-annotation-query-issued-every-dashboard": "활성화되면 대시보드를 새로 고칠 때마다 주석 쿼리가 실행됩니다", - "description-hidden": "주석 쿼리는 대시보드 상단에서 토글하여 켜거나 끌 수 있습니다. 이 옵션을 선택하면 이 토글이 숨겨집니다.", + "label-annotation-controls-display": "", "label-color": "색상", "label-data-source": "데이터 소스", "label-enabled": "활성화됨", - "label-hidden": "숨김", "label-name": "이름", "label-show-in": "표시 위치", "placeholder-choose-panels": "패널 선택", @@ -5865,6 +5877,7 @@ "label-open-link-in-new-tab": "새 탭에서 링크 열기", "label-options": "옵션", "label-show-as-dropdown": "드롭다운으로 표시", + "label-show-in-controls-menu": "", "label-title": "제목", "label-tooltip": "툴팁", "label-type": "유형", @@ -6424,6 +6437,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "변수 편집기 양식", "back-to-list": "목록으로 돌아가기", @@ -6738,8 +6770,8 @@ "test": "테스트" }, "cloud-info-box": { - "body-alert": "또는 이 작업을 건너뛰고 <6>Grafana Cloud 평생 무료 플랜을 통해 Grafana Labs에서 완전 관리되고 확장 가능하며 호스팅되는 데이터 소스인 {{mainDS}}(및 {{extraDS}})을(를) 받으세요.", - "title-alert": "아래에서 {{mainDS}} 데이터 소스를 구성하세요" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "대시보드 삭제", diff --git a/public/locales/nl-NL/grafana.json b/public/locales/nl-NL/grafana.json index d4a2ffc07e1..126ad3f53f9 100644 --- a/public/locales/nl-NL/grafana.json +++ b/public/locales/nl-NL/grafana.json @@ -5824,14 +5824,26 @@ }, "annotation-settings-edit": { "back-to-list": "Terug naar lijst", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Verwijderen", "description-color-annotation-event-markers": "Te gebruiken kleur voor het markeren van gebeurtenisannotaties", "description-enabled-annotation-query-issued-every-dashboard": "Wanneer ingeschakeld, wordt de annotatiequery elke dashboardvernieuwing uitgegeven", - "description-hidden": "Annotatiequery's kunnen bovenaan het dashboard worden in- of uitgeschakeld. Als deze optie is aangevinkt, wordt deze schakelaar verborgen.", + "label-annotation-controls-display": "", "label-color": "Kleur", "label-data-source": "Gegevensbron", "label-enabled": "Ingeschakeld", - "label-hidden": "Verborgen", "label-name": "Naam", "label-show-in": "Weergeven in", "placeholder-choose-panels": "Panelen kiezen", @@ -5887,6 +5899,7 @@ "label-open-link-in-new-tab": "Koppeling openen in nieuw tabblad", "label-options": "Opties", "label-show-as-dropdown": "Weergeven als dropdown", + "label-show-in-controls-menu": "", "label-title": "Titel", "label-tooltip": "Tooltip", "label-type": "Type", @@ -6448,6 +6461,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Formulier variabele bewerker", "back-to-list": "Terug naar lijst", @@ -6762,8 +6794,8 @@ "test": "Testen" }, "cloud-info-box": { - "body-alert": "Of bespaar je de moeite en krijg {{mainDS}} (en {{extraDS}}) als volledig beheerde, schaalbare en gehoste gegevensbronnen van Grafana Labs met het <6>altijd gratis Grafana Cloud-abonnement.", - "title-alert": "Configureer hieronder je gegevensbron voor {{mainDS}}" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Dashboard verwijderen", diff --git a/public/locales/pl-PL/grafana.json b/public/locales/pl-PL/grafana.json index b2cd07c639a..0f441a67b12 100644 --- a/public/locales/pl-PL/grafana.json +++ b/public/locales/pl-PL/grafana.json @@ -5868,14 +5868,26 @@ }, "annotation-settings-edit": { "back-to-list": "Powrót do listy", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Usuń", "description-color-annotation-event-markers": "Kolor używany dla znaczników zdarzeń związanych z adnotacją", "description-enabled-annotation-query-issued-every-dashboard": "Po włączeniu zapytanie dotyczące adnotacji jest wysyłane przy każdym odświeżeniu pulpitu", - "description-hidden": "Zapytania dotyczące adnotacji można włączyć lub wyłączyć u góry pulpitu. Po zaznaczeniu tej opcji przełącznik zostanie ukryty.", + "label-annotation-controls-display": "", "label-color": "Kolor", "label-data-source": "Źródło danych", "label-enabled": "Włączone", - "label-hidden": "Ukryte", "label-name": "Imię", "label-show-in": "Pokaż w", "placeholder-choose-panels": "Wybierz panele", @@ -5931,6 +5943,7 @@ "label-open-link-in-new-tab": "Otwórz link w nowej karcie", "label-options": "Opcje", "label-show-as-dropdown": "Pokaż jako listę rozwijaną", + "label-show-in-controls-menu": "", "label-title": "Tytuł", "label-tooltip": "Podpowiedź", "label-type": "Typ", @@ -6496,6 +6509,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Formularz edytora zmiennych", "back-to-list": "Powrót do listy", @@ -6810,8 +6842,8 @@ "test": "Test" }, "cloud-info-box": { - "body-alert": "Możesz ułatwić sobie życie i wybrać {{mainDS}} (oraz {{extraDS}}) jako w pełni zarządzane, skalowalne źródła danych hostowane przez Grafana Labs w ramach <6>planu Grafana Cloud, który będzie zawsze bezpłatny.", - "title-alert": "Skonfiguruj źródło danych {{mainDS}} poniżej" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Usuń pulpit", diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json index 67946425e20..a96c531b03c 100644 --- a/public/locales/pt-BR/grafana.json +++ b/public/locales/pt-BR/grafana.json @@ -5824,14 +5824,26 @@ }, "annotation-settings-edit": { "back-to-list": "Voltar para a lista", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Excluir", "description-color-annotation-event-markers": "Cor que será usada para os marcadores de evento de anotação", "description-enabled-annotation-query-issued-every-dashboard": "Quando ativada, a consulta de anotação é emitida a cada atualização do painel", - "description-hidden": "As consultas de anotação podem ser ativadas ou desativadas na parte superior do painel. Com esta opção marcada, este botão de alternância ficará oculto.", + "label-annotation-controls-display": "", "label-color": "Cor", "label-data-source": "Fonte de dados", "label-enabled": "Ativado", - "label-hidden": "Oculto", "label-name": "Nome", "label-show-in": "Exibir em", "placeholder-choose-panels": "Escolher painéis", @@ -5887,6 +5899,7 @@ "label-open-link-in-new-tab": "Abrir link em nova aba", "label-options": "Opções", "label-show-as-dropdown": "Exibir como menu suspenso", + "label-show-in-controls-menu": "", "label-title": "Título", "label-tooltip": "Dica de uso", "label-type": "Tipo", @@ -6448,6 +6461,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Formulário do editor de variáveis", "back-to-list": "Voltar para a lista", @@ -6762,8 +6794,8 @@ "test": "Teste" }, "cloud-info-box": { - "body-alert": "Ou evite essa tarefa e obtenha {{mainDS}} (e {{extraDS}}) como fontes de dados totalmente gerenciadas, escaláveis e hospedadas da Grafana Labs com o <6>plano gratuito vitalício da Grafana Cloud.", - "title-alert": "Configure sua fonte de {{mainDS}} dados abaixo" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Excluir painel de controle", diff --git a/public/locales/pt-PT/grafana.json b/public/locales/pt-PT/grafana.json index f9a8b59e8c3..dc7e3c11c34 100644 --- a/public/locales/pt-PT/grafana.json +++ b/public/locales/pt-PT/grafana.json @@ -5824,14 +5824,26 @@ }, "annotation-settings-edit": { "back-to-list": "Voltar à lista", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Eliminar", "description-color-annotation-event-markers": "Cor a utilizar para os marcadores de eventos de anotação", "description-enabled-annotation-query-issued-every-dashboard": "Quando esta opção está ativada, a consulta de anotação é emitida a cada atualização do painel de controlo", - "description-hidden": "As consultas de anotação podem ser ativadas ou desativadas na parte superior do painel de controlo. Com esta opção marcada, esta alternância ficará oculta.", + "label-annotation-controls-display": "", "label-color": "Cor", "label-data-source": "Origem dos dados", "label-enabled": "Ativado", - "label-hidden": "Oculto", "label-name": "Nome", "label-show-in": "Mostrar em", "placeholder-choose-panels": "Escolher painéis", @@ -5887,6 +5899,7 @@ "label-open-link-in-new-tab": "Abrir o link num novo separador", "label-options": "Opções", "label-show-as-dropdown": "Mostrar como menu suspenso", + "label-show-in-controls-menu": "", "label-title": "Título", "label-tooltip": "Descrição", "label-type": "Tipo", @@ -6448,6 +6461,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Formulário do editor de variáveis", "back-to-list": "Voltar à lista", @@ -6762,8 +6794,8 @@ "test": "Teste" }, "cloud-info-box": { - "body-alert": "Ou evite o esforço e obtenha {{mainDS}} (e {{extraDS}}) como origens de dados totalmente geridas, escaláveis e hospedadas da Grafana Labs com o <6>plano Grafana Cloud gratuito para sempre.", - "title-alert": "Configurar a sua origem de dados {{mainDS}} abaixo" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Eliminar painel de controlo", diff --git a/public/locales/ru-RU/grafana.json b/public/locales/ru-RU/grafana.json index 39d4bb3e010..cb1af71f6ed 100644 --- a/public/locales/ru-RU/grafana.json +++ b/public/locales/ru-RU/grafana.json @@ -5868,14 +5868,26 @@ }, "annotation-settings-edit": { "back-to-list": "Назад к списку", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Удалить", "description-color-annotation-event-markers": "Цвет, который будет использоваться для маркеров событий аннотаций.", "description-enabled-annotation-query-issued-every-dashboard": "Если этот параметр включен, запрос аннотации выдается при каждом обновлении панели.", - "description-hidden": "Запросы аннотаций можно включать или отключать в верхней части панели. Если установлен этот параметр, переключатель будет скрыт.", + "label-annotation-controls-display": "", "label-color": "Цвет", "label-data-source": "Источник данных", "label-enabled": "Включено", - "label-hidden": "Скрыто", "label-name": "Имя", "label-show-in": "Показать в", "placeholder-choose-panels": "Выбрать панели", @@ -5931,6 +5943,7 @@ "label-open-link-in-new-tab": "Открыть ссылку в новой вкладке", "label-options": "Параметры", "label-show-as-dropdown": "Показывать как раскрывающийся список", + "label-show-in-controls-menu": "", "label-title": "Название", "label-tooltip": "Подсказка", "label-type": "Тип", @@ -6496,6 +6509,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Форма редактора переменных", "back-to-list": "Назад к списку", @@ -6810,8 +6842,8 @@ "test": "Тестирование" }, "cloud-info-box": { - "body-alert": "Или не тратьте время и получите {{mainDS}} (и {{extraDS}}) в качестве полностью управляемых, масштабируемых и размещенных источников данных от Grafana Labs с <6>бессрочным бесплатным планом Grafana Cloud.", - "title-alert": "Настройте свой источник данных {{mainDS}} ниже" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Удалить дашборд", diff --git a/public/locales/sv-SE/grafana.json b/public/locales/sv-SE/grafana.json index 4cd8fffafd8..883cac40ddf 100644 --- a/public/locales/sv-SE/grafana.json +++ b/public/locales/sv-SE/grafana.json @@ -5824,14 +5824,26 @@ }, "annotation-settings-edit": { "back-to-list": "Tillbaka till listan", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Ta bort", "description-color-annotation-event-markers": "Färg som ska användas för kommentarshändelsemarkörer", "description-enabled-annotation-query-issued-every-dashboard": "När aktiverat utfärdas kommentarsfrågan vid varje uppdatering av instrumentpanelen", - "description-hidden": "Frågor om kommentarer kan aktiveras eller inaktiveras högst upp på instrumentpanelen. Med det här alternativet markerat kommer denna växel att döljas.", + "label-annotation-controls-display": "", "label-color": "Färg", "label-data-source": "Datakälla", "label-enabled": "Aktiverad", - "label-hidden": "Dolt", "label-name": "Namn", "label-show-in": "Visa i", "placeholder-choose-panels": "Välj paneler", @@ -5887,6 +5899,7 @@ "label-open-link-in-new-tab": "Öppna länken i en ny flik", "label-options": "Alternativ", "label-show-as-dropdown": "Visa som rullgardinsmeny", + "label-show-in-controls-menu": "", "label-title": "Titel", "label-tooltip": "Verktygstips", "label-type": "Typ", @@ -6448,6 +6461,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Formulär för variabelsredigering", "back-to-list": "Tillbaka till listan", @@ -6762,8 +6794,8 @@ "test": "Test" }, "cloud-info-box": { - "body-alert": "Eller hoppa över ansträngningen och få {{mainDS}} (och {{extraDS}}) som fullt hanterade, skalbara och värdbaserade datakällor från Grafana Labs med <6>Grafana Cloud-prenumerationen gratis för alltid.", - "title-alert": "Konfigurera din {{mainDS}}-datakälla nedan" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Ta bort instrumentpanel", diff --git a/public/locales/tr-TR/grafana.json b/public/locales/tr-TR/grafana.json index 2d3751c4e2e..2037475ec8a 100644 --- a/public/locales/tr-TR/grafana.json +++ b/public/locales/tr-TR/grafana.json @@ -5824,14 +5824,26 @@ }, "annotation-settings-edit": { "back-to-list": "Listeye geri dön", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "Sil", "description-color-annotation-event-markers": "Ek açıklama olay işaretçileri için kullanılacak renk", "description-enabled-annotation-query-issued-every-dashboard": "Etkinleştirildiğinde her pano yenilemesinde açıklama sorgusu çalıştırılır", - "description-hidden": "Ek açıklama sorguları, panonun üst kısmından açılabilir veya kapatılabilir. Bu seçenek işaretlendiğinde bu açma/kapama gizlenecektir.", + "label-annotation-controls-display": "", "label-color": "Renk", "label-data-source": "Veri kaynağı", "label-enabled": "Etkin", - "label-hidden": "Gizli", "label-name": "Ad", "label-show-in": "Şurada göster:", "placeholder-choose-panels": "Panel seçin", @@ -5887,6 +5899,7 @@ "label-open-link-in-new-tab": "Bağlantıyı yeni sekmede aç", "label-options": "Seçenekler", "label-show-as-dropdown": "Açılır menü olarak göster", + "label-show-in-controls-menu": "", "label-title": "Başlık", "label-tooltip": "Araç ipucu", "label-type": "Tür", @@ -6448,6 +6461,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "Değişken düzenleyici formu", "back-to-list": "Listeye geri dön", @@ -6762,8 +6794,8 @@ "test": "Test" }, "cloud-info-box": { - "body-alert": "Veya uğraşmadan {{mainDS}} (ve {{extraDS}}) veri kaynaklarını Grafana Labs'tan tamamen yönetilen, ölçeklenebilir ve barındırılan <6>daima ücretsiz Grafana Cloud planıyla edinin.", - "title-alert": "Aşağıdan {{mainDS}} veri kaynağınızı yapılandırın" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "Panoyu sil", diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index 96e44a5792c..8d34dcd8286 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -5802,14 +5802,26 @@ }, "annotation-settings-edit": { "back-to-list": "回到列表", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "删除", "description-color-annotation-event-markers": "用于注释事件标记的颜色", "description-enabled-annotation-query-issued-every-dashboard": "启用后,每次数据面板刷新时都会发出注释查询", - "description-hidden": "可在数据面板顶部打开或关闭注释查询。选中此选项后,此切换开关将被隐藏。", + "label-annotation-controls-display": "", "label-color": "颜色", "label-data-source": "数据源", "label-enabled": "已启用", - "label-hidden": "隐藏", "label-name": "名称", "label-show-in": "显示于", "placeholder-choose-panels": "选择面板", @@ -5865,6 +5877,7 @@ "label-open-link-in-new-tab": "在新选项卡中打开链接", "label-options": "选项", "label-show-as-dropdown": "显示为下拉", + "label-show-in-controls-menu": "", "label-title": "标题", "label-tooltip": "工具提示", "label-type": "类型", @@ -6424,6 +6437,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "变量编辑器表单", "back-to-list": "回到列表", @@ -6738,8 +6770,8 @@ "test": "测试" }, "cloud-info-box": { - "body-alert": "您也可以省去这些步骤,通过<6>永久免费的 Grafana Cloud 计划,从 Grafana Labs 获取完全托管、可扩展和托管式数据源 {{mainDS}}(和 {{extraDS}})。", - "title-alert": "在下方配置您的 {{mainDS}} 数据源" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "删除数据面板", diff --git a/public/locales/zh-Hant/grafana.json b/public/locales/zh-Hant/grafana.json index c2fea7e8d27..97699c7bdd8 100644 --- a/public/locales/zh-Hant/grafana.json +++ b/public/locales/zh-Hant/grafana.json @@ -5802,14 +5802,26 @@ }, "annotation-settings-edit": { "back-to-list": "返回清單", + "control-display-options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "description": "", + "label": "" + } + }, "delete": "刪除", "description-color-annotation-event-markers": "用於註解事件標記的顏色", "description-enabled-annotation-query-issued-every-dashboard": "啟用後,每次儀表板重新整理時都會發出註解查詢", - "description-hidden": "可以在儀表板頂部開啟或關閉註解查詢。選擇此選項後,此切換選項將被隱藏。", + "label-annotation-controls-display": "", "label-color": "顏色", "label-data-source": "資料來源", "label-enabled": "已啟用", - "label-hidden": "隱藏", "label-name": "名稱(名字)", "label-show-in": "顯示在", "placeholder-choose-panels": "選擇面板", @@ -5865,6 +5877,7 @@ "label-open-link-in-new-tab": "用新分頁開啟連結", "label-options": "選項", "label-show-as-dropdown": "顯示為下拉式", + "label-show-in-controls-menu": "", "label-title": "標題", "label-tooltip": "提示", "label-type": "類型", @@ -6424,6 +6437,25 @@ "variable-controls": { "add-variable": "" }, + "variable-display-select": { + "label": "", + "options": { + "above-dashboard": { + "label": "" + }, + "controls-menu": { + "description": "", + "label": "" + }, + "hidden": { + "label": "" + }, + "hidden-label": { + "description": "", + "label": "" + } + } + }, "variable-editor-form": { "aria-label-variable-editor-form": "變數編輯器表單", "back-to-list": "返回清單", @@ -6738,8 +6770,8 @@ "test": "測試" }, "cloud-info-box": { - "body-alert": "或者,跳過這個步驟,並透過<6>永久免費的 Grafana Cloud 方案,從 Grafana Labs 取得完全受控、可擴展及託管的資料來源 {{mainDS}}(和 {{extraDS}})。", - "title-alert": "在下方設定您的 {{mainDS}} 資料來源" + "body-alert": "", + "title-alert": "" }, "dashboards-table": { "aria-label-delete-dashboard": "刪除儀表板", From b3648f0823a04a706c7bff3383ef8db4eef7c938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 5 Dec 2025 08:09:41 +0100 Subject: [PATCH 14/48] chore: introduce toggle for decoupling plugins from bootdata (#114890) chore: toggle for decoupling plugins from bootdata --- .../grafana-data/src/types/featureToggles.gen.ts | 5 +++++ pkg/services/featuremgmt/registry.go | 8 ++++++++ pkg/services/featuremgmt/toggles_gen.csv | 1 + pkg/services/featuremgmt/toggles_gen.json | 16 +++++++++++++++- 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index f95bb2d4356..3fab1ac8bd5 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -1211,4 +1211,9 @@ export interface FeatureToggles { * Adds support for Kubernetes alerting historian APIs */ kubernetesAlertingHistorian?: boolean; + /** + * Enables plugins decoupling from bootdata + * @default false + */ + useMTPlugins?: boolean; } diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index b121f58cb42..6bdd7e26871 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -2000,6 +2000,14 @@ var ( Owner: grafanaAlertingSquad, RequiresRestart: true, }, + { + Name: "useMTPlugins", + Description: "Enables plugins decoupling from bootdata", + Stage: FeatureStageExperimental, + Owner: grafanaPluginsPlatformSquad, + FrontendOnly: true, + Expression: "false", + }, } ) diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 1287a38870e..1a3f6d03a27 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -271,3 +271,4 @@ ttlPluginInstanceManager,experimental,@grafana/plugins-platform-backend,false,fa lokiQueryLimitsContext,experimental,@grafana/observability-logs,false,false,true rudderstackUpgrade,experimental,@grafana/grafana-frontend-platform,false,false,true kubernetesAlertingHistorian,experimental,@grafana/alerting-squad,false,true,false +useMTPlugins,experimental,@grafana/plugins-platform-backend,false,false,true diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index ec1bce7682b..0b84e7b0bbd 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -3558,6 +3558,20 @@ "frontend": true } }, + { + "metadata": { + "name": "useMTPlugins", + "resourceVersion": "1764913709691", + "creationTimestamp": "2025-12-05T05:48:29Z" + }, + "spec": { + "description": "Enables plugins decoupling from bootdata", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend", + "frontend": true, + "expression": "false" + } + }, { "metadata": { "name": "useMultipleScopeNodesEndpoint", @@ -3674,4 +3688,4 @@ } } ] -} +} \ No newline at end of file From a8d174ccefd924ed1cc8b1e33e3d7325615e657b Mon Sep 17 00:00:00 2001 From: Pepe Cano <825430+ppcano@users.noreply.github.com> Date: Fri, 5 Dec 2025 08:28:16 +0100 Subject: [PATCH 15/48] docs(alerting): add new Examples of trace-based alerts (#114511) * docs(alerting): add new Examples of trace-based alerts * fix vale issues --- .../best-practices/dynamic-thresholds.md | 2 +- .../best-practices/trace-based-alerts.md | 382 ++++++++++++++++++ 2 files changed, 383 insertions(+), 1 deletion(-) create mode 100644 docs/sources/alerting/best-practices/trace-based-alerts.md diff --git a/docs/sources/alerting/best-practices/dynamic-thresholds.md b/docs/sources/alerting/best-practices/dynamic-thresholds.md index 8cbc3ada2b0..e749bc1d943 100644 --- a/docs/sources/alerting/best-practices/dynamic-thresholds.md +++ b/docs/sources/alerting/best-practices/dynamic-thresholds.md @@ -12,7 +12,7 @@ labels: - oss menuTitle: Examples of dynamic thresholds title: Example of dynamic thresholds per dimension -weight: 1103 +weight: 1105 refs: testdata-data-source: - pattern: /docs/grafana/ diff --git a/docs/sources/alerting/best-practices/trace-based-alerts.md b/docs/sources/alerting/best-practices/trace-based-alerts.md new file mode 100644 index 00000000000..d908ecd7385 --- /dev/null +++ b/docs/sources/alerting/best-practices/trace-based-alerts.md @@ -0,0 +1,382 @@ +--- +canonical: https://grafana.com/docs/grafana/latest/alerting/best-practices/trace-based-alerts/ +description: This guide provides introductory examples and distinct approaches for setting up trace-based alerts in Grafana. +keywords: + - grafana +labels: + products: + - cloud + - enterprise + - oss +title: Examples of trace-based alerts +weight: 1103 +refs: + testdata-data-source: + - pattern: /docs/grafana/ + destination: /docs/grafana//datasources/testdata/ + - pattern: /docs/grafana-cloud/ + destination: /docs/grafana-cloud/connect-externally-hosted/data-sources/testdata/ +--- + +# Examples of trace-based alerts + +Metrics are the foundation of most alerting systems. They are usually the first signal that something is wrong, but they don’t always indicate _where_ or _why_ a failure occurs. + +Traces fill that gap by showing the complete path a request takes through your system. They map the workflows across services, indicating where the request slows down or fails. + +{{< figure src="/media/docs/alerting/screenshot-traces-visualization-11.5.png" max-width="750px" alt="Trace view" >}} + +Traces report duration and errors directly to specific services and spans, helping to find the affected component and service scope. With this additional context, alerting on tracing data can help you **identify root causes faster**. + +You can create trace-based alerts in Grafana Alerting using two main approaches: + +- Querying metrics generated from tracing data. +- Using TraceQL, a query language for traces available in Grafana Tempo. + +This guide provides introductory examples and distinct approaches for setting up **trace-based alerts** in Grafana. Tracing data is commonly collected using **OpenTelemetry (OTel)** instrumentation. OTel allows you to integrate trace data from a wide range of applications and environments into Grafana. + +## **Alerting on span metrics** + +OpenTelemetry provides processors that convert tracing data into Prometheus-style metrics. + +The **service graph** and **span metrics** processors are the standard options in Alloy and Tempo to generate Prometheus metrics from traces. They can generate the rate, error, and duration (RED) metrics from sampled spans. + +You can then create alert rules that query metrics derived from traces. + +{{< figure src="/media/docs/alerting/why-trace-based-metrics.png" max-width="750px" alt="Why metrics if you have traces?" >}} + +[Service graph metrics](https://grafana.com/docs/tempo/latest/metrics-from-traces/service_graphs/) focus on inter-service communication and dependency health. They measure the calls between services, helping Grafana to infer the service topology. However, they measure only the interaction between two services—they don’t include the internal processing time of the client service. + +You can use service graph metrics to detect infrastructure issues such as network degradation or service mesh problems. + +For trace-based alerts, we recommend using [span metrics](https://grafana.com/docs/tempo/latest/metrics-from-traces/span-metrics/). + +**Span metrics** measure the total processing time of a service request: capturing what happens inside the service, not just the communication between services. They include the time spent on internal processing and waiting on downstream calls, providing an **end-to-end picture of service performance**. + +Depending on how you create span metrics, the following span metrics are generated: + +| Span metrics generator | Metric name | Prometheus metric type | Description | +| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------- | :---------------------------- | :--------------------------- | +| [Alloy](https://grafana.com/docs/alloy/latest/reference/components/otelcol/otelcol.connector.spanmetrics/) and [OTEL span metrics connector](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/connector/spanmetricsconnector) | `traces_span_metrics_calls_total` | Counter | Total count of the span | +| | `traces_span_metrics_duration_seconds` | Histogram (native or classic) | Duration of the span | +| [Tempo](https://grafana.com/docs/tempo/latest/metrics-from-traces/span-metrics/span-metrics-metrics-generator/) and [Grafana Cloud Application Observability](https://grafana.com/docs/grafana-cloud/monitor-applications/application-observability/setup/metrics-labels/) | `traces_spanmetrics_calls_total` | Counter | Total count of the span | +| | `traces_spanmetrics_latency` | Histogram (native or classic) | Duration of the span | +| | `traces_spanmetrics_size_total` | Counter | Total size of spans ingested | + +Each metric includes by default the following labels: `service`, `span_name`, `span_kind`, `status_code`, `status_message`, `job`, and `instance`. + +In the metrics generator, you can customize how traces are converted into metrics by configuring histograms, exemplars, metric dimensions, and other options. + +The following examples assume that span metrics have already been generated using one of these options or an alternative. + +### Detect slow span operations + +This example shows how to define an alert rule that detects when operations handled by a service become slow. + +Before looking at the query, it’s useful to review a few [trace elements](https://grafana.com/docs/tempo/latest/introduction/trace-structure/) that shape how it works: + +- A trace represents a single request or transaction as it flows through multiple spans and services. A span refers to a specific operation within a service. +- Each span includes the operation name (`span_name`) and its duration (the metric value), as well as additional fields like [span status](https://opentelemetry.io/docs/concepts/signals/traces/#span-status) (`status_code`) and [span kind](https://opentelemetry.io/docs/concepts/signals/traces/#span-kind) (`span_kind`). +- A server span represents work performed on the receiving side of a request, while a client span represents the outbound call (parent span) waiting for a response (client → server). + +To detect slow inbound operations within a specific service, you can define an alert rule that detects when the percentile latency of server spans exceeds a threshold. For example: + +_Detect when 95% of requests (excluding errors) do not complete faster than 2 seconds._ + +#### Using native histograms + +The following PromQL query uses the `traces_span_metrics_duration_seconds` native histogram metric to define the alert rule query. + +```promql +histogram_quantile(0.95, + sum by (span_name) ( + rate(traces_span_metrics_duration_seconds{ + service_name="", + span_kind="SPAN_KIND_SERVER", + status_code!="STATUS_CODE_ERROR" + }[10m]) + ) +) > 2 +``` + +Here’s the query breakdown + +- `traces_span_metrics_duration_seconds` + It’s a native histogram produced from spans using Alloy or the OTEL collector. The metric is filtered by: + - `service_name=""` targets a particular service. + - `span_kind="SPAN_KIND_SERVER"` selects spans handling inbound requests. + - `status_code!="STATUS_CODE_ERROR"` excludes spans that ended with errors. + + _You should query `traces_spanmetrics_latency` when using other span metric generators._ + +- `rate(...[10m])` + Converts the histogram into a per-second histogram over the last 10 minutes (the distribution of spans per second during that period). + This makes the time window explicit and ensures latencies can be calculated over the last 10 minutes using `histogram_*` functions. +- `sum by (span_name)( … )` + Merges all series that share the same `span_name`. This creates a [multidimensional alert](https://grafana.com/docs/grafana/latest/alerting/best-practices/multi-dimensional-alerts/) that generates one alert instance per span name (operation). +- `histogram_quantile(0.95, ...)` + Calculates p95 latency from the histogram after applying the rate. + The query runs as an **instant Prometheus query**, returning a single value for the 10-minute window. +- `> 2` + Defines the threshold condition. It returns only series whose p95 latency exceeds 2 seconds. + Alternatively, you can set this threshold as a Grafana Alerting expression in the UI, as shown in the following screenshot. + + {{< figure src="/media/docs/alerting/trace-based-alertrule-screenshot.png" max-width="750px" caption="Alert rule querying span metrics and using threshold expression" >}} + +#### Using classic histograms + +Native histograms are stable in Prometheus since v3.8.0. Your span metric generator may therefore create classic histograms for latency span metrics, either `traces_span_metrics_duration_seconds` or `traces_spanmetrics_latency`. + +When using classic histograms, the metric is the same but the metric format changes. A classic histogram represents a histogram with fixed buckets and exposes three metrics: + +- `_bucket`: cumulative buckets of the observations. +- `_sum`: total sum of all observed values. +- `_count`: count of observed values. + +To calculate percentiles accurately, especially exceeding a particular threshold (e.g. `` `2s` ``), you have to configure the classic histogram with the explicit bucket, such as: + +```shell +["100ms", "250ms", "1s", "2s", "5s"] +``` + +The `otelcol.connector.spanmetrics` can configure the buckets using the [`explicit` block](https://grafana.com/docs/alloy/latest/reference/components/otelcol/otelcol.connector.spanmetrics/#explicit). The metric-generator in Tempo can configure the [`span_metrics.histogram_buckets` setting](https://grafana.com/docs/tempo/latest/configuration/#metrics-generator). + +Here's the equivalent PromQL for classic histograms: + +```promql +histogram_quantile(0.95, + sum by (span_name, le) ( + rate(traces_span_metrics_duration_seconds_bucket{ + service_name="", + span_kind="SPAN_KIND_SERVER", + status_code!="STATUS_CODE_ERROR" + }[10m]) + ) +) > 2 +``` + +Key differences compared with the native histograms example: + +- You must configure a histogram bucket matching the desired threshold (for example, `2s`). +- You must query the `_bucket` metric, not the base metric. +- You must include `le` in the `sum by (…)` grouping for `histogram_quantile` calculation. + +Everything else remains the same. + +{{< admonition type="note" >}} + +The alert rules in these examples create [multi-dimensional alerts](https://grafana.com/docs/grafana/latest/alerting/best-practices/multi-dimensional-alerts/): one alert instance for each distinct span name. + +Dynamic span routes such as `/product/1234` can create separate metric dimensions and alerts for each unique span, which can significantly impact metric costs and performance for large volumes. + +To prevent high-cardinality data, normalize dynamic routes like `/product/{id}` using semantic attributes such as [`http.route`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/http/) and [`url.template`](https://opentelemetry.io/docs/specs/semconv/registry/attributes/url/), and limit dimensions to low-cardinality fields such as `service_name`, `status_code`, or `http_method`. + +{{< /admonition >}} + +### Detect high error rate + +This example defines an alert rule that detects when the error rate for any operation exceeds 20%. You can use this error rate alerts to identify increases in request errors, such as 5xx responses or internal failures. + +The following query calculates the fraction of failed server spans for each service and operation. + +```promql +( + sum by (service, span_name) ( + rate(traces_span_metrics_calls_total{ + span_kind="SPAN_KIND_SERVER", + status_code="STATUS_CODE_ERROR" + }[10m]) + ) +/ + sum by (service, span_name) ( + rate(traces_span_metrics_calls_total{ + span_kind="SPAN_KIND_SERVER" + }[10m]) + ) +) > 0.2 +``` + +Here’s the query breakdown + +- `traces_span_metrics_calls_total` + A counter metric produced from spans that tracks the number of completed span operations. + - `span_kind="SPAN_KIND_SERVER"` selects spans handling inbound requests. + - `status_code="STATUS_CODE_ERROR"` selects only spans that ended in error. + - Omitting the `status_code` filter in the denominator includes all spans, returning the total span count. + + _Check whether your metric generator instead creates the `traces_spanmetrics_calls_total` metric, and adjust the metric name._ + +- `rate(...[10m])` + Converts the cumulative histogram into a per-second histogram over the last 10 minutes (the distribution of spans per second during that period). + This makes the time window explicit and ensures counters can be calculated over the last 10 minutes. +- `sum by (service, span_name)( … )` + Aggregates per service and operation, creating one alert instance for each `(service, span_name)` combination. + This is a [multidimensional alert](https://grafana.com/docs/grafana/latest/alerting/best-practices/multi-dimensional-alerts/) that applies to all services, helping identify which service and corresponding operation is failing. +- `sum by () (...) / sum by () (...)` + Divides failed spans by total spans to calculate the error rate per operation. + The result is a ratio between `0` and `1,` where `1` means all operations failed. + The query runs as an **instant Prometheus query**, returning a single value for the 10-minute window. +- `> 0.2` + Defines the threshold condition. It returns only series whose error rate is higher than 20% of spans. + Alternatively, you can set this threshold as a Grafana Alerting expression in the UI. + +### Enable traffic guardrails + +When the traffic is very low, even a single slow or failing request can trigger the alerts. + +To avoid these types of false positives during low-traffic periods, you can include a **minimum traffic condition** in your alert rule queries. For example: + +```promql +sum by (service, span_name)( + increase(traces_span_metrics_calls_total{ + span_kind="SPAN_KIND_SERVER" + }[10m]) +) > 300 +``` + +This query returns only spans that handled more than 300 requests in the 10-minute period. + +This minimum level of traffic helps prevent false positives, ensuring the alert evaluates a significant number of spans before triggering. + +You can combine this traffic condition with the **error-rate** query to ensure alerts fire only when both conditions are met: + +```promql +(( + sum by (service, span_name) ( + rate(traces_span_metrics_calls_total{ + span_kind="SPAN_KIND_SERVER", + status_code="STATUS_CODE_ERROR" + }[10m]) + ) +/ + sum by (service, span_name) ( + rate(traces_span_metrics_calls_total{ + span_kind="SPAN_KIND_SERVER" + }[10m]) + ) +) > 0.2) +and +( + sum by (service, span_name)( + increase(traces_span_metrics_calls_total{ + span_kind="SPAN_KIND_SERVER" + }[10m]) +) > 300 ) + +``` + +For a given span, the alert fires when: + +- The **error rate exceeds 20%** over the last 10 minutes. +- The span **handled at least 300 requests** over the last 10 minutes. + +**Alternatively**, you can split the alert into separate queries and combine them using a math expression as the threshold. In the example below, `$ErrorRateCondition` is the Grafana reference for the error-rate query, and `$TrafficCondition` is the reference for the traffic query. + +{{< figure src="/media/docs/alerting/traffic-guardrail-with-separate-queries.png" max-width="500px" alt="Alert rule with threshold based on two queries" >}} + +In this case, you must ensure both queries group by the same labels. + +The advantage of this approach is that you can observe the results of both independent queries. You can then access the query results through the [`$values` variable](https://grafana.com/docs/grafana/latest/alerting/alerting-rules/templates/reference/#values) and display them in notifications or use them in custom labels. + +A potential drawback of splitting queries is that each query runs separately. This increases backend load and can affect query performance, especially in environments with a large number of active alerts. + +You can apply this traffic guardrail pattern to any alert rule. + +### Consider sampling {#consider-sampling} + +[Sampling](https://grafana.com/docs/tempo/latest/set-up-for-tracing/instrument-send/set-up-collector/tail-sampling/) is a technique used to reduce the amount of collected spans for cost-saving purposes. There are two main strategies which can be combined: + +- **Head sampling**: The decision to record or drop a span is made when the trace begins. The condition can be configured probabilistically (a percentage of traces) or by filtering out certain operations. +- **Tail sampling**: The decision is made after the trace completes. This allows sampling more interesting operations, such as slow or failing requests. + +With **head sampling**, alerting on span metrics should be done with caution, since span metrics will represent only a subset of all traces. + +With **tail sampling**, it’s important to generate span metrics before a sampling decision is made. [Grafana Cloud Adaptive Traces](https://grafana.com/docs/grafana-cloud/adaptive-telemetry/adaptive-traces/) handle this automatically. With Alloy or the OpenTelemetry Collector, make sure the SpanMetrics connector runs before the filtering or [tail sampling processor](https://grafana.com/docs/alloy/latest/reference/components/otelcol/otelcol.processor.tail_sampling/). + +## **Using TraceQL (experimental)** + +**TraceQL** is a query language for searching and filtering traces in **Grafana Tempo**, which uses a syntax similar to `PromQL` and `LogQL`. + +With TraceQL, you can skip converting tracing data into span metrics and query raw trace data directly. It provides a more flexible filtering based on the trace structure, attributes, or resource metadata, and can detect issues faster as it does not wait for metric generation. + +However, keep in mind that TraceQL is not suitable for all scenarios. For example: + +- **Inadequate for long-term analysis** + Trace data has a significantly shorter retention period than metrics. For historical monitoring, it’s recommended to convert key tracing data into metrics to ensure the persistence of important data. +- **Inadequate for alerting after sampling** + TraceQL can only query traces that are actually stored in Tempo. If sampling drops a large portion of traces, TraceQL-based alerts may miss real issues. Refer to [consider sampling](#consider-sampling) for guidance on how to generate span metrics before sampling. + + {{< admonition type="caution" >}} + + TraceQL alerting is available in Grafana v12.1 or higher, supported as an [experimental feature](https://grafana.com/docs/release-life-cycle/). + Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. + + While TraceQL can be powerful for exploring and detecting issues directly from trace data, **alerting with TraceQL should not be used in production environments yet**. Use it for testing and experimentation at this moment. + + {{< /admonition >}} + +The following example demonstrates how to recreate the previous **alert rule that detected slow span operations** using TraceQL. + +Follow these steps to create the alert: + +1. Enable TraceQL alerting + To use TraceQL in alerts, you must enable the [**`tempoAlerting`** feature flag in your Grafana configuration](https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#feature_toggles). + +2. Configure the alert query + + In your alert rule, select the **Tempo** data source, then convert the original PromQL query into the equivalent TraceQL query: + + ```traceql + {status != error && kind = server && .service.name = ""} + | quantile_over_time(duration, .95) by (name) + ``` + + For a given service, this query calculates the **p95 latency** for all server spans, excluding errors, and groups them by span name. + +3. Configure the time range + + Currently, TraceQL alerting supports only range queries. + To define the time window, set the query time range to **the last 10 minutes.** + - From: `now-10m` + - To: `now` + + {{< figure src="/media/docs/alerting/traceql-alert-configure-time-range.png" max-width="750px" alt="Time range configuration for TraceQL alert rule" >}} + +4. Add a reducer expression. + + Range queries return time series data, not a single value. The alert rule must then **reduce** time series data to a single numeric value before comparing it against a threshold. + + Add a **Reduce** expression to convert the query results into a single value. + +5. Set the threshold condition. + + Create a **Threshold** expression to fire when the p95 latency exceeds 2 seconds: **$B > 2**. + + {{< figure src="/media/docs/alerting/traceql-alert-configure-threshold.png" max-width="750px" alt="Alert rule configuration showing reducer and threshold expressions for TraceQL query" >}} + +This final alert detects when 95% of the server spans for a particular service (excluding errors) take longer than 2 seconds to complete, using raw trace data instead of span metrics. + +## Additional resources + +To explore related topics and expand the examples in this guide, see the following resources: + +- [Trace structure](https://grafana.com/docs/tempo/latest/introduction/trace-structure/): Learn how traces and spans are structured. + +- [Grafana Tempo documentation](https://grafana.com/docs/tempo/latest/): Full reference for Grafana’s open source tracing backend. + +- [Span metrics using the metrics generator in Tempo](https://grafana.com/docs/tempo/latest/metrics-from-traces/span-metrics/span-metrics-metrics-generator/): Generate span metrics directly from traces with Tempo’s built-in metrics generator. + +- [Span metrics using Grafana Alloy](https://grafana.com/docs/tempo/latest/metrics-from-traces/span-metrics/span-metrics-alloy/): Configure Alloy to export span metrics from OpenTelemetry (OTel) traces. + +- [Multi-dimensional alerts](https://grafana.com/docs/grafana/latest/alerting/best-practices/multi-dimensional-alerts/): Learn how to trigger multiple alert instances per alert rule like in these examples. + +- [Grafana SLO documentation](https://grafana.com/docs/grafana-cloud/alerting-and-irm/slo/): Use span metrics to define Service Level Objectives (SLOs) in Grafana. +- [Trace sampling](https://grafana.com/docs/tempo/latest/set-up-for-tracing/instrument-send/set-up-collector/tail-sampling/#sampling): explore strategies and configuration in Grafana Tempo. + + {{< admonition type="note" >}} + + OpenTelemetry instrumentations can record metrics independently of spans. + + These [OTEL metrics](https://opentelemetry.io/docs/specs/semconv/general/metrics/) are not derived from traces and are not affected by sampling. They can serve as an alternative to span-derived metrics. + + {{< /admonition >}} From 88478a851e5df15b4c8a3f604d986d2e4e471d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Fri, 5 Dec 2025 08:29:48 +0100 Subject: [PATCH 16/48] chore: reduce Loki barrel files (#114888) --- eslint-suppressions.json | 5 ----- public/app/features/explore/state/query.ts | 2 +- public/app/features/logs/logsModel.ts | 2 +- .../plugins/datasource/loki/LanguageProvider.test.ts | 3 ++- .../app/plugins/datasource/loki/LanguageProvider.ts | 3 ++- .../app/plugins/datasource/loki/LogContextProvider.ts | 3 ++- .../datasource/loki/backendResultTransformer.ts | 3 ++- .../datasource/loki/components/LokiOptionFields.tsx | 3 ++- .../loki/components/LokiQueryEditor.test.tsx | 3 ++- .../plugins/datasource/loki/components/stats.test.ts | 2 +- .../app/plugins/datasource/loki/components/stats.ts | 2 +- public/app/plugins/datasource/loki/datasource.test.ts | 3 ++- public/app/plugins/datasource/loki/datasource.ts | 11 ++--------- .../plugins/datasource/loki/querySplitting.test.ts | 3 ++- public/app/plugins/datasource/loki/querySplitting.ts | 3 ++- public/app/plugins/datasource/loki/queryUtils.test.ts | 3 ++- public/app/plugins/datasource/loki/queryUtils.ts | 3 ++- .../components/LokiQueryBuilderOptions.test.tsx | 3 ++- .../components/LokiQueryBuilderOptions.tsx | 3 ++- .../app/plugins/datasource/loki/querybuilder/state.ts | 3 ++- .../datasource/loki/shardQuerySplitting.test.ts | 3 ++- public/app/plugins/datasource/loki/tracking.ts | 3 ++- public/app/plugins/datasource/loki/types.ts | 2 -- 23 files changed, 38 insertions(+), 36 deletions(-) diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 5c863244c20..492d5e18958 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -4080,11 +4080,6 @@ "count": 2 } }, - "public/app/plugins/datasource/loki/types.ts": { - "no-barrel-files/no-barrel-files": { - "count": 3 - } - }, "public/app/plugins/datasource/mixed/module.ts": { "no-barrel-files/no-barrel-files": { "count": 2 diff --git a/public/app/features/explore/state/query.ts b/public/app/features/explore/state/query.ts index 251ab4af5d2..39ad4ea5e73 100644 --- a/public/app/features/explore/state/query.ts +++ b/public/app/features/explore/state/query.ts @@ -36,7 +36,7 @@ import { getShiftedTimeRange } from 'app/core/utils/timePicker'; import { getCorrelationsBySourceUIDs } from 'app/features/correlations/utils'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getFiscalYearStartMonth, getTimeZone } from 'app/features/profile/state/selectors'; -import { SupportingQueryType } from 'app/plugins/datasource/loki/types'; +import { SupportingQueryType } from 'app/plugins/datasource/loki/dataquery.gen'; import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource'; import { ExploreItemState, diff --git a/public/app/features/logs/logsModel.ts b/public/app/features/logs/logsModel.ts index e60887518fe..8a0ff6b99ad 100644 --- a/public/app/features/logs/logsModel.ts +++ b/public/app/features/logs/logsModel.ts @@ -44,7 +44,7 @@ import { t } from '@grafana/i18n'; import { BarAlignment, GraphDrawStyle, StackingMode } from '@grafana/schema'; import { colors } from '@grafana/ui'; import { getThemeColor } from 'app/core/utils/colors'; -import { LokiQueryDirection } from 'app/plugins/datasource/loki/types'; +import { LokiQueryDirection } from 'app/plugins/datasource/loki/dataquery.gen'; import { LogsFrame, parseLogsFrame } from './logsFrame'; import { createLogRowsMap, getLogLevel, getLogLevelFromKey, sortInAscendingOrder } from './utils'; diff --git a/public/app/plugins/datasource/loki/LanguageProvider.test.ts b/public/app/plugins/datasource/loki/LanguageProvider.test.ts index 02a365c7d9a..447911c70a5 100644 --- a/public/app/plugins/datasource/loki/LanguageProvider.test.ts +++ b/public/app/plugins/datasource/loki/LanguageProvider.test.ts @@ -2,6 +2,7 @@ import { AbstractLabelOperator, DataFrame, TimeRange, dateTime, ScopedVars } fro import { config } from '@grafana/runtime'; import LanguageProvider from './LanguageProvider'; +import { LokiQueryType } from './dataquery.gen'; import { DEFAULT_MAX_LINES_SAMPLE, LokiDatasource } from './datasource'; import { createDetectedFieldValuesMetadataRequest } from './mocks/createDetectedFieldValuesMetadataRequest'; import { createDetectedFieldsMetadataRequest } from './mocks/createDetectedFieldsMetadataRequest'; @@ -12,7 +13,7 @@ import { extractLabelKeysFromDataFrame, extractUnwrapLabelKeysFromDataFrame, } from './responseUtils'; -import { DetectedFieldsResult, LabelType, LokiQueryType } from './types'; +import { DetectedFieldsResult, LabelType } from './types'; jest.mock('./responseUtils'); diff --git a/public/app/plugins/datasource/loki/LanguageProvider.ts b/public/app/plugins/datasource/loki/LanguageProvider.ts index 9b86f01796d..a6851e474d4 100644 --- a/public/app/plugins/datasource/loki/LanguageProvider.ts +++ b/public/app/plugins/datasource/loki/LanguageProvider.ts @@ -4,6 +4,7 @@ import { LRUCache } from 'lru-cache'; import { AbstractQuery, getDefaultTimeRange, KeyValue, LanguageProvider, ScopedVars, TimeRange } from '@grafana/data'; import { BackendSrvRequest, config } from '@grafana/runtime'; +import { LokiQueryType } from './dataquery.gen'; import { DEFAULT_MAX_LINES_SAMPLE, LokiDatasource } from './datasource'; import { abstractQueryToExpr, mapAbstractOperatorsToOp, processLabels } from './languageUtils'; import { getStreamSelectorsFromQuery } from './queryUtils'; @@ -13,7 +14,7 @@ import { extractLogParserFromDataFrame, extractUnwrapLabelKeysFromDataFrame, } from './responseUtils'; -import { DetectedFieldsResult, LabelType, LokiQuery, LokiQueryType, ParserAndLabelKeysResult } from './types'; +import { DetectedFieldsResult, LabelType, LokiQuery, ParserAndLabelKeysResult } from './types'; const NS_IN_MS = 1000000; const EMPTY_SELECTOR = '{}'; diff --git a/public/app/plugins/datasource/loki/LogContextProvider.ts b/public/app/plugins/datasource/loki/LogContextProvider.ts index 3de58ba2e4a..83d22fa49fe 100644 --- a/public/app/plugins/datasource/loki/LogContextProvider.ts +++ b/public/app/plugins/datasource/loki/LogContextProvider.ts @@ -19,6 +19,7 @@ import { import { LabelParser, LabelFilter, LineFilters, PipelineStage, Logfmt, Json } from '@grafana/lezer-logql'; import { LokiContextUi } from './components/LokiContextUi'; +import { LokiQueryDirection, LokiQueryType } from './dataquery.gen'; import { LokiDatasource, makeRequest, REF_ID_STARTER_LOG_ROW_CONTEXT } from './datasource'; import { escapeLabelValueInExactSelector, getLabelTypeFromFrame } from './languageUtils'; import { addLabelToQuery, addParserToQuery } from './modifyQuery'; @@ -29,7 +30,7 @@ import { isQueryWithParser, } from './queryUtils'; import { sortDataFrameByTime, SortDirection } from './sortDataFrame'; -import { ContextFilter, LabelType, LokiQuery, LokiQueryDirection, LokiQueryType } from './types'; +import { ContextFilter, LabelType, LokiQuery } from './types'; export const LOKI_LOG_CONTEXT_PRESERVED_LABELS = 'lokiLogContextPreservedLabels'; export const SHOULD_INCLUDE_PIPELINE_OPERATIONS = 'lokiLogContextShouldIncludePipelineOperations'; diff --git a/public/app/plugins/datasource/loki/backendResultTransformer.ts b/public/app/plugins/datasource/loki/backendResultTransformer.ts index 1cc91c4c621..6ed0ee1d288 100644 --- a/public/app/plugins/datasource/loki/backendResultTransformer.ts +++ b/public/app/plugins/datasource/loki/backendResultTransformer.ts @@ -1,10 +1,11 @@ import { DataQueryResponse, DataFrame, isDataFrame, FieldType, QueryResultMeta, DataQueryError } from '@grafana/data'; +import { LokiQueryType } from './dataquery.gen'; import { getDerivedFields } from './getDerivedFields'; import { makeTableFrames } from './makeTableFrames'; import { getExpressionFromExecutedQuery, getHighlighterExpressionsFromQuery } from './queryUtils'; import { dataFrameHasLokiError } from './responseUtils'; -import { DerivedFieldConfig, LokiQuery, LokiQueryType } from './types'; +import { DerivedFieldConfig, LokiQuery } from './types'; function isMetricFrame(frame: DataFrame): boolean { return frame.fields.every((field) => field.type === FieldType.time || field.type === FieldType.number); diff --git a/public/app/plugins/datasource/loki/components/LokiOptionFields.tsx b/public/app/plugins/datasource/loki/components/LokiOptionFields.tsx index 01aeb2c0574..3e773722f88 100644 --- a/public/app/plugins/datasource/loki/components/LokiOptionFields.tsx +++ b/public/app/plugins/datasource/loki/components/LokiOptionFields.tsx @@ -5,7 +5,8 @@ import { SelectableValue } from '@grafana/data'; import { config, reportInteraction } from '@grafana/runtime'; import { InlineField, Input, Stack } from '@grafana/ui'; -import { LokiQuery, LokiQueryDirection, LokiQueryType } from '../types'; +import { LokiQueryType, LokiQueryDirection } from '../dataquery.gen'; +import { LokiQuery } from '../types'; export interface LokiOptionFieldsProps { lineLimitValue: string; diff --git a/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx b/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx index 5897d65018c..3d228199ac8 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx @@ -5,9 +5,10 @@ import { cloneDeep, defaultsDeep } from 'lodash'; import { CoreApp } from '@grafana/data'; import { QueryEditorMode } from '@grafana/plugin-ui'; +import { LokiQueryType } from '../dataquery.gen'; import { createLokiDatasource } from '../mocks/datasource'; import { EXPLAIN_LABEL_FILTER_CONTENT } from '../querybuilder/components/LokiQueryBuilderExplained'; -import { LokiQuery, LokiQueryType } from '../types'; +import { LokiQuery } from '../types'; import { LokiQueryEditor } from './LokiQueryEditor'; import { LokiQueryEditorProps } from './types'; diff --git a/public/app/plugins/datasource/loki/components/stats.test.ts b/public/app/plugins/datasource/loki/components/stats.test.ts index 89535558ef1..4fea9703117 100644 --- a/public/app/plugins/datasource/loki/components/stats.test.ts +++ b/public/app/plugins/datasource/loki/components/stats.test.ts @@ -1,6 +1,6 @@ import { dateTime, getDefaultTimeRange } from '@grafana/data'; -import { LokiQueryType } from '../types'; +import { LokiQueryType } from '../dataquery.gen'; import { shouldUpdateStats } from './stats'; diff --git a/public/app/plugins/datasource/loki/components/stats.ts b/public/app/plugins/datasource/loki/components/stats.ts index 1b35497c67f..3a3d3f9188e 100644 --- a/public/app/plugins/datasource/loki/components/stats.ts +++ b/public/app/plugins/datasource/loki/components/stats.ts @@ -1,6 +1,6 @@ import { DateTime, isDateTime, TimeRange } from '@grafana/data'; -import { LokiQueryType } from '../types'; +import { LokiQueryType } from '../dataquery.gen'; /** * This function compares two time values. If the first is absolute, it compares them using `DateTime.isSame`. diff --git a/public/app/plugins/datasource/loki/datasource.test.ts b/public/app/plugins/datasource/loki/datasource.test.ts index 359e1716055..533ce1ce23c 100644 --- a/public/app/plugins/datasource/loki/datasource.test.ts +++ b/public/app/plugins/datasource/loki/datasource.test.ts @@ -33,11 +33,12 @@ import { } from '@grafana/runtime'; import { LokiVariableSupport } from './LokiVariableSupport'; +import { LokiQueryType, SupportingQueryType } from './dataquery.gen'; import { LokiDatasource, REF_ID_DATA_SAMPLES } from './datasource'; import { createLokiDatasource } from './mocks/datasource'; import { createMetadataRequest } from './mocks/metadataRequest'; import { runSplitQuery } from './querySplitting'; -import { LokiOptions, LokiQuery, LokiQueryType, LokiVariableQueryType, SupportingQueryType } from './types'; +import { LokiOptions, LokiQuery, LokiVariableQueryType } from './types'; jest.mock('@grafana/runtime', () => { return { diff --git a/public/app/plugins/datasource/loki/datasource.ts b/public/app/plugins/datasource/loki/datasource.ts index c7b60f169a5..fa047772588 100644 --- a/public/app/plugins/datasource/loki/datasource.ts +++ b/public/app/plugins/datasource/loki/datasource.ts @@ -54,6 +54,7 @@ import { LokiVariableSupport } from './LokiVariableSupport'; import { transformBackendResult } from './backendResultTransformer'; import { LokiAnnotationsQueryEditor } from './components/AnnotationsQueryEditor'; import { placeHolderScopedVars } from './components/monaco-query-field/monaco-completion-provider/validation'; +import { LokiQueryType, SupportingQueryType } from './dataquery.gen'; import { escapeLabelValueInSelector, isRegexSelector, getLabelTypeFromFrame } from './languageUtils'; import { labelNamesRegex, labelValuesRegex } from './migrations/variableQueryMigrations'; import { @@ -88,15 +89,7 @@ import { replaceVariables, returnVariables } from './querybuilder/parsingUtils'; import { runShardSplitQuery } from './shardQuerySplitting'; import { convertToWebSocketUrl, doLokiChannelStream } from './streaming'; import { trackQuery } from './tracking'; -import { - LokiOptions, - LokiQuery, - LokiQueryType, - LokiVariableQuery, - LokiVariableQueryType, - QueryStats, - SupportingQueryType, -} from './types'; +import { LokiOptions, LokiQuery, LokiVariableQuery, LokiVariableQueryType, QueryStats } from './types'; export type RangeQueryOptions = DataQueryRequest | AnnotationQueryRequest; export const DEFAULT_MAX_LINES = 1000; diff --git a/public/app/plugins/datasource/loki/querySplitting.test.ts b/public/app/plugins/datasource/loki/querySplitting.test.ts index 2d04b447005..fdadf7c0965 100644 --- a/public/app/plugins/datasource/loki/querySplitting.test.ts +++ b/public/app/plugins/datasource/loki/querySplitting.test.ts @@ -3,13 +3,14 @@ import { of } from 'rxjs'; import { DataQueryError, DataQueryRequest, DataQueryResponse, dateTime, LoadingState } from '@grafana/data'; import { config } from '@grafana/runtime'; +import { LokiQueryType, LokiQueryDirection } from './dataquery.gen'; import { LokiDatasource } from './datasource'; import { createLokiDatasource } from './mocks/datasource'; import { getMockFrames } from './mocks/frames'; import { runSplitQuery } from './querySplitting'; import { LOKI_MAX_QUERY_BYTES_READ_ERROR_MSG_PREFIX, LOKI_TIMEOUT_ERROR_MSG } from './responseUtils'; import { trackGroupedQueries } from './tracking'; -import { LokiQuery, LokiQueryDirection, LokiQueryType } from './types'; +import { LokiQuery } from './types'; jest.mock('./tracking'); jest.mock('uuid', () => ({ diff --git a/public/app/plugins/datasource/loki/querySplitting.ts b/public/app/plugins/datasource/loki/querySplitting.ts index 89a466b21d0..4d383955b3f 100644 --- a/public/app/plugins/datasource/loki/querySplitting.ts +++ b/public/app/plugins/datasource/loki/querySplitting.ts @@ -15,6 +15,7 @@ import { } from '@grafana/data'; import { config } from '@grafana/runtime'; +import { LokiQueryType, LokiQueryDirection } from './dataquery.gen'; import { LokiDatasource } from './datasource'; import { splitTimeRange as splitLogsTimeRange } from './logsTimeSplitting'; import { combineResponses } from './mergeResponses'; @@ -22,7 +23,7 @@ import { splitTimeRange as splitMetricTimeRange } from './metricTimeSplitting'; import { addQueryLimitsContext, isLogsQuery, isQueryWithRangeVariable } from './queryUtils'; import { isRetriableError } from './responseUtils'; import { trackGroupedQueries } from './tracking'; -import { LokiGroupedRequest, LokiQuery, LokiQueryDirection, LokiQueryType } from './types'; +import { LokiGroupedRequest, LokiQuery } from './types'; export function partitionTimeRange( isLogsQuery: boolean, diff --git a/public/app/plugins/datasource/loki/queryUtils.test.ts b/public/app/plugins/datasource/loki/queryUtils.test.ts index e901b77c55b..9ff9a87e3d6 100644 --- a/public/app/plugins/datasource/loki/queryUtils.test.ts +++ b/public/app/plugins/datasource/loki/queryUtils.test.ts @@ -1,5 +1,6 @@ import { String } from '@grafana/lezer-logql'; +import { LokiQueryType, LokiQueryDirection } from './dataquery.gen'; import { getHighlighterExpressionsFromQuery, getLokiQueryType, @@ -20,7 +21,7 @@ import { interpolateShardingSelector, requestSupportsSharding, } from './queryUtils'; -import { LokiQuery, LokiQueryDirection, LokiQueryType } from './types'; +import { LokiQuery } from './types'; describe('getHighlighterExpressionsFromQuery', () => { it('returns no expressions for empty query', () => { diff --git a/public/app/plugins/datasource/loki/queryUtils.ts b/public/app/plugins/datasource/loki/queryUtils.ts index a4a895feae5..6b158edbe48 100644 --- a/public/app/plugins/datasource/loki/queryUtils.ts +++ b/public/app/plugins/datasource/loki/queryUtils.ts @@ -29,9 +29,10 @@ import { } from '@grafana/lezer-logql'; import { DataQuery } from '@grafana/schema'; +import { LokiQueryType, LokiQueryDirection } from './dataquery.gen'; import { addDropToQuery, addLabelToQuery, getStreamSelectorPositions, NodePosition } from './modifyQuery'; import { ErrorId } from './querybuilder/parsingUtils'; -import { LabelType, LokiQuery, LokiQueryDirection, LokiQueryType } from './types'; +import { LabelType, LokiQuery } from './types'; /** * Returns search terms from a LogQL query. diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx index c03de3eb2e8..50a5c64f1a3 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.test.tsx @@ -4,8 +4,9 @@ import userEvent from '@testing-library/user-event'; import { CoreApp, LogSortOrderChangeEvent, LogsSortOrder, store } from '@grafana/data'; import { config, getAppEvents } from '@grafana/runtime'; +import { LokiQueryType, LokiQueryDirection } from '../../dataquery.gen'; import { createLokiDatasource } from '../../mocks/datasource'; -import { LokiQuery, LokiQueryDirection, LokiQueryType } from '../../types'; +import { LokiQuery } from '../../types'; import { LokiQueryBuilderOptions, Props } from './LokiQueryBuilderOptions'; diff --git a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx index 27d199cb8a5..27a6dbbe533 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/LokiQueryBuilderOptions.tsx @@ -14,9 +14,10 @@ import { queryTypeOptions, } from '../../components/LokiOptionFields'; import { placeHolderScopedVars } from '../../components/monaco-query-field/monaco-completion-provider/validation'; +import { LokiQueryDirection, LokiQueryType } from '../../dataquery.gen'; import { LokiDatasource } from '../../datasource'; import { getLokiQueryType, isLogsQuery } from '../../queryUtils'; -import { LokiQuery, LokiQueryDirection, LokiQueryType, QueryStats } from '../../types'; +import { LokiQuery, QueryStats } from '../../types'; export interface Props { query: LokiQuery; diff --git a/public/app/plugins/datasource/loki/querybuilder/state.ts b/public/app/plugins/datasource/loki/querybuilder/state.ts index 5f8aaecad55..dcd70852465 100644 --- a/public/app/plugins/datasource/loki/querybuilder/state.ts +++ b/public/app/plugins/datasource/loki/querybuilder/state.ts @@ -1,6 +1,7 @@ import { QueryEditorMode } from '@grafana/plugin-ui'; -import { LokiQuery, LokiQueryType } from '../types'; +import { LokiQueryType } from '../dataquery.gen'; +import { LokiQuery } from '../types'; const queryEditorModeDefaultLocalStorageKey = 'LokiQueryEditorModeDefault'; diff --git a/public/app/plugins/datasource/loki/shardQuerySplitting.test.ts b/public/app/plugins/datasource/loki/shardQuerySplitting.test.ts index cebee5bee94..0196fd90d64 100644 --- a/public/app/plugins/datasource/loki/shardQuerySplitting.test.ts +++ b/public/app/plugins/datasource/loki/shardQuerySplitting.test.ts @@ -3,12 +3,13 @@ import { of } from 'rxjs'; import { DataQueryRequest, DataQueryResponse, dateTime, LoadingState } from '@grafana/data'; import { config } from '@grafana/runtime'; +import { LokiQueryDirection, LokiQueryType } from './dataquery.gen'; import { LokiDatasource } from './datasource'; import { createLokiDatasource } from './mocks/datasource'; import { getMockFrames } from './mocks/frames'; import { LOKI_MAX_QUERY_BYTES_READ_ERROR_MSG_PREFIX, LOKI_TIMEOUT_ERROR_MSG } from './responseUtils'; import { runShardSplitQuery } from './shardQuerySplitting'; -import { LokiQuery, LokiQueryDirection, LokiQueryType } from './types'; +import { LokiQuery } from './types'; jest.mock('uuid', () => ({ v4: jest.fn().mockReturnValue('uuid'), diff --git a/public/app/plugins/datasource/loki/tracking.ts b/public/app/plugins/datasource/loki/tracking.ts index 5714d8b4e5a..85d12446e6d 100644 --- a/public/app/plugins/datasource/loki/tracking.ts +++ b/public/app/plugins/datasource/loki/tracking.ts @@ -2,6 +2,7 @@ import { CoreApp, DashboardLoadedEvent, DataQueryRequest, DataQueryResponse } fr import { QueryEditorMode } from '@grafana/plugin-ui'; import { reportInteraction, config } from '@grafana/runtime'; +import { LokiQueryType } from './dataquery.gen'; import { REF_ID_STARTER_ANNOTATION, REF_ID_DATA_SAMPLES, @@ -12,7 +13,7 @@ import { import pluginJson from './plugin.json'; import { getNormalizedLokiQuery, isLogsQuery, obfuscate } from './queryUtils'; import { variableRegex } from './querybuilder/parsingUtils'; -import { LokiGroupedRequest, LokiQuery, LokiQueryType } from './types'; +import { LokiGroupedRequest, LokiQuery } from './types'; type LokiOnDashboardLoadedTrackingEvent = { grafana_version?: string; diff --git a/public/app/plugins/datasource/loki/types.ts b/public/app/plugins/datasource/loki/types.ts index ae650931390..6c2e0d2deea 100644 --- a/public/app/plugins/datasource/loki/types.ts +++ b/public/app/plugins/datasource/loki/types.ts @@ -7,8 +7,6 @@ import { LokiQueryDirection, } from './dataquery.gen'; -export { LokiQueryDirection, LokiQueryType, SupportingQueryType }; - export enum LokiResultType { Stream = 'streams', Vector = 'vector', From c74af430a608a8feb75f49308a6c215625cafdc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 5 Dec 2025 08:48:39 +0100 Subject: [PATCH 17/48] Gauge: Only show spotlight in dark themes (#114524) * Gauge: Only show spotlight in dark themes * Update --- .../components/RadialGauge/RadialGauge.tsx | 2 +- .../plugins/panel/radialbar/EffectsEditor.tsx | 23 +++++++++++-------- public/locales/en-US/grafana.json | 3 ++- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx index 3dcc25e8875..96e8b0f50f3 100644 --- a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx +++ b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx @@ -135,7 +135,7 @@ export function RadialGauge(props: RadialGaugeProps) { displayProcessor, }); - if (spotlight) { + if (spotlight && theme.isDark) { defs.push( ) { /> - - props.onChange({ ...props.value, spotlight: e.currentTarget.checked })} - /> - - + + + props.onChange({ ...props.value, spotlight: e.currentTarget.checked })} + /> + + + + ); } diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 81c9583040e..6ee3b9e50e6 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -12396,7 +12396,8 @@ "bar-glow": "Bar glow", "center-glow": "Center glow", "rounded-bars": "Rounded bars", - "spotlight": "Spotlight" + "spotlight": "Spotlight", + "spotlight-tooltip": "Only visible in dark themes" }, "gradient": "Gradient", "gradient-auto": "Auto", From 0a51779107a238cf0e5029b01ceaabc398353728 Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Fri, 5 Dec 2025 11:42:17 +0100 Subject: [PATCH 18/48] MSSQL: Remove the need for `azure_auth_enabled` (#114775) Remove the need for azure_auth_enabled for MSSQL --- .../datasources/mssql/configure/index.md | 4 --- .../configuration/ConfigurationEditor.tsx | 26 ++++++------------- public/app/plugins/datasource/mssql/types.ts | 1 - 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/docs/sources/datasources/mssql/configure/index.md b/docs/sources/datasources/mssql/configure/index.md index 4d3eb60ee81..7ce6398f1cc 100644 --- a/docs/sources/datasources/mssql/configure/index.md +++ b/docs/sources/datasources/mssql/configure/index.md @@ -153,10 +153,6 @@ If you're using an older version of Microsoft SQL Server like 2008 and 2008R2, y **Authentication:** -{{< admonition type="note" >}} -In order to use Azure AD Authentication the toggle `auth.azure_auth_enabled` must be set to `true` in the Grafana configuration file. -{{< /admonition >}} - | Authentication Type | Description | Credentials / Fields | | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **SQL Server Authentication** | Default method to connect to MSSQL. Use a SQL Server or Windows login in `DOMAIN\User` format. | - **Username**: SQL Server username
- **Password**: SQL Server password | diff --git a/public/app/plugins/datasource/mssql/configuration/ConfigurationEditor.tsx b/public/app/plugins/datasource/mssql/configuration/ConfigurationEditor.tsx index 707e1b0bb0d..5a80db52824 100644 --- a/public/app/plugins/datasource/mssql/configuration/ConfigurationEditor.tsx +++ b/public/app/plugins/datasource/mssql/configuration/ConfigurationEditor.tsx @@ -49,10 +49,8 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps - {azureAuthIsSupported && ( -
  • - - Azure Authentication Securely authenticate and access Azure resources and applications using - Azure AD credentials - Managed Service Identity and Client Secret Credentials are supported. - -
  • - )} +
  • + + Azure Authentication Securely authenticate and access Azure resources and applications using + Azure AD credentials - Managed Service Identity and Client Secret Credentials are supported. + +
  • Windows AD: Username + password Windows Active Directory - Sign on for domain user via @@ -393,7 +383,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps )} - {azureAuthIsSupported && jsonData.authenticationType === MSSQLAuthenticationType.azureAuth && ( + {jsonData.authenticationType === MSSQLAuthenticationType.azureAuth && (
    diff --git a/public/app/plugins/datasource/mssql/types.ts b/public/app/plugins/datasource/mssql/types.ts index c4ed9763cec..4e784c38877 100644 --- a/public/app/plugins/datasource/mssql/types.ts +++ b/public/app/plugins/datasource/mssql/types.ts @@ -39,6 +39,5 @@ export interface MssqlSecureOptions { } export type AzureAuthConfigType = { - azureAuthIsSupported: boolean; azureAuthSettingsUI: (props: HttpSettingsBaseProps) => JSX.Element; }; From be9978117667949cd3857650778b68acfdb8fe82 Mon Sep 17 00:00:00 2001 From: Sergej-Vlasov <37613182+Sergej-Vlasov@users.noreply.github.com> Date: Fri, 5 Dec 2025 11:01:43 +0000 Subject: [PATCH 19/48] TransformSaveModelToScene: Force v1 for reports (#114767) force v1 for reports --- .../pages/DashboardScenePageStateManager.ts | 2 +- .../transformSaveModelToScene.ts | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts index 7f8173f9cd9..3a1841e6b32 100644 --- a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts +++ b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts @@ -441,7 +441,7 @@ export class DashboardScenePageStateManager extends DashboardScenePageStateManag } if (rsp?.dashboard) { - const scene = transformSaveModelToScene(rsp); + const scene = transformSaveModelToScene(rsp, options); // Special handling for Template route - set up edit mode and dirty state if ( diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index 3c334b022c1..48763ba4363 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -29,11 +29,11 @@ import { } from 'app/features/dashboard/services/DashboardProfiler'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; -import { DashboardDTO, DashboardDataDTO } from 'app/types/dashboard'; +import { DashboardDTO, DashboardDataDTO, DashboardRoutes } from 'app/types/dashboard'; import { addPanelsOnLoadBehavior } from '../addToDashboard/addPanelsOnLoadBehavior'; import { dashboardAnalyticsInitializer } from '../behaviors/DashboardAnalyticsInitializerBehavior'; -import { shouldForceV2API } from '../pages/DashboardScenePageStateManager'; +import { LoadDashboardOptions, shouldForceV2API } from '../pages/DashboardScenePageStateManager'; import { AlertStatesDataLayer } from '../scene/AlertStatesDataLayer'; import { DashboardAnnotationsDataLayer } from '../scene/DashboardAnnotationsDataLayer'; import { DashboardControls } from '../scene/DashboardControls'; @@ -76,11 +76,11 @@ export interface SaveModelToSceneOptions { isEmbedded?: boolean; } -export function transformSaveModelToScene(rsp: DashboardDTO): DashboardScene { +export function transformSaveModelToScene(rsp: DashboardDTO, options?: LoadDashboardOptions): DashboardScene { // Just to have migrations run const oldModel = new DashboardModel(rsp.dashboard, rsp.meta); - const scene = createDashboardSceneFromDashboardModel(oldModel, rsp.dashboard); + const scene = createDashboardSceneFromDashboardModel(oldModel, rsp.dashboard, options); // TODO: refactor createDashboardSceneFromDashboardModel to work on Dashboard schema model const apiVersion = config.featureToggles.kubernetesDashboards @@ -255,12 +255,17 @@ function createRowItemFromLegacyRow(row: PanelModel, panels: DashboardGridItem[] return rowItem; } -export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel, dto: DashboardDataDTO) { +export function createDashboardSceneFromDashboardModel( + oldModel: DashboardModel, + dto: DashboardDataDTO, + options?: LoadDashboardOptions +) { let variables: SceneVariableSet | undefined; let annotationLayers: SceneDataLayerProvider[] = []; let alertStatesLayer: AlertStatesDataLayer | undefined; const uid = oldModel.uid; - const serializerVersion = shouldForceV2API() && !oldModel.meta.isSnapshot ? 'v2' : 'v1'; + const isReport = options?.route === DashboardRoutes.Report; + const serializerVersion = shouldForceV2API() && !oldModel.meta.isSnapshot && !isReport ? 'v2' : 'v1'; if (oldModel.meta.isSnapshot) { variables = createVariablesForSnapshot(oldModel); @@ -348,7 +353,7 @@ export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel, let body: DashboardLayoutManager; - if (config.featureToggles.dashboardNewLayouts && oldModel.panels.some((p) => p.type === 'row')) { + if (serializerVersion === 'v2' && oldModel.panels.some((p) => p.type === 'row')) { body = createRowsFromPanels(oldModel.panels); } else { body = new DefaultGridLayoutManager({ From 75eb820c73785ff272b1aee9c14d458cc0c3c031 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Fri, 5 Dec 2025 15:01:11 +0300 Subject: [PATCH 20/48] Folders: update manifest (avoid useoldmanifestkinds) (#114827) --- apps/advisor/kinds/cue.mod/module.cue | 2 +- .../historian/kinds/cue.mod/module.cue | 4 +- .../notifications/kinds/cue.mod/module.cue | 2 +- apps/annotation/kinds/cue.mod/module.cue | 4 +- apps/collections/kinds/cue.mod/module.cue | 2 +- apps/correlations/kinds/cue.mod/module.cue | 4 +- apps/dashboard/kinds/cue.mod/module.cue | 2 +- apps/example/kinds/cue.mod/module.cue | 4 +- apps/folder/Makefile | 5 +- apps/folder/go.mod | 25 ---- apps/folder/go.sum | 65 ---------- apps/folder/kinds/cue.mod/module.cue | 2 +- apps/folder/kinds/folder.cue | 27 ++-- apps/folder/kinds/manifest.cue | 16 ++- .../apis/folder/v1beta1/folder_client_gen.go | 19 --- .../apis/folder/v1beta1/folder_object_gen.go | 28 +---- .../apis/folder/v1beta1/folder_status_gen.go | 3 - .../folder/v1beta1/zz_generated.openapi.go | 10 +- apps/folder/pkg/apis/folder_manifest.go | 116 ------------------ apps/iam/kinds/cue.mod/module.cue | 2 +- apps/investigations/kinds/cue.mod/module.cue | 2 +- apps/logsdrilldown/kinds/cue.mod/module.cue | 4 +- apps/playlist/kinds/cue.mod/module.cue | 2 +- apps/plugins/kinds/cue.mod/module.cue | 2 +- apps/preferences/kinds/cue.mod/module.cue | 2 +- apps/provisioning/kinds/cue.mod/module.cue | 2 +- apps/secret/kinds/cue.mod/module.cue | 2 +- apps/shorturl/kinds/cue.mod/module.cue | 4 +- .../rtkq/folder/v1beta1/endpoints.gen.ts | 2 - pkg/tests/apis/folder/folders_test.go | 6 +- .../folder.grafana.app-v1beta1.json | 15 +-- .../app/api/clients/folder/v1beta1/hooks.ts | 2 - .../Folders/NewProvisionedFolderForm.test.tsx | 1 - 33 files changed, 58 insertions(+), 330 deletions(-) delete mode 100644 apps/folder/pkg/apis/folder/v1beta1/folder_status_gen.go delete mode 100644 apps/folder/pkg/apis/folder_manifest.go diff --git a/apps/advisor/kinds/cue.mod/module.cue b/apps/advisor/kinds/cue.mod/module.cue index eec596c8515..20d5e478697 100644 --- a/apps/advisor/kinds/cue.mod/module.cue +++ b/apps/advisor/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/apps/advisor/kinds" language: { - version: "v0.9.0" + version: "v0.11.0" } diff --git a/apps/alerting/historian/kinds/cue.mod/module.cue b/apps/alerting/historian/kinds/cue.mod/module.cue index 2f45dd38aec..e3ca09eba43 100644 --- a/apps/alerting/historian/kinds/cue.mod/module.cue +++ b/apps/alerting/historian/kinds/cue.mod/module.cue @@ -1,2 +1,4 @@ module: "github.com/grafana/grafana/apps/alerting/historian/kinds" -language: version: "v0.8.2" +language: { + version: "v0.11.0" +} diff --git a/apps/alerting/notifications/kinds/cue.mod/module.cue b/apps/alerting/notifications/kinds/cue.mod/module.cue index f8a02e23e2f..d5af2d37220 100644 --- a/apps/alerting/notifications/kinds/cue.mod/module.cue +++ b/apps/alerting/notifications/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/apps/alerting/notifications/kinds" language: { - version: "v0.9.0" + version: "v0.11.0" } diff --git a/apps/annotation/kinds/cue.mod/module.cue b/apps/annotation/kinds/cue.mod/module.cue index a7f0a62536f..3e191cc59b7 100644 --- a/apps/annotation/kinds/cue.mod/module.cue +++ b/apps/annotation/kinds/cue.mod/module.cue @@ -1,2 +1,4 @@ module: "github.com/grafana/grafana/apps/annotation/kinds" -language: version: "v0.8.2" +language: { + version: "v0.11.0" +} diff --git a/apps/collections/kinds/cue.mod/module.cue b/apps/collections/kinds/cue.mod/module.cue index 6280fd8f227..9675fc32a51 100644 --- a/apps/collections/kinds/cue.mod/module.cue +++ b/apps/collections/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/apps/preferences/kinds" language: { - version: "v0.9.0" + version: "v0.11.0" } diff --git a/apps/correlations/kinds/cue.mod/module.cue b/apps/correlations/kinds/cue.mod/module.cue index 2238cffd9a5..ba35847baeb 100644 --- a/apps/correlations/kinds/cue.mod/module.cue +++ b/apps/correlations/kinds/cue.mod/module.cue @@ -1,2 +1,4 @@ module: "github.com/grafana/grafana/apps/correlations/kinds" -language: version: "v0.8.2" +language: { + version: "v0.11.0" +} diff --git a/apps/dashboard/kinds/cue.mod/module.cue b/apps/dashboard/kinds/cue.mod/module.cue index a3b88f8aa81..f8c354c37fb 100644 --- a/apps/dashboard/kinds/cue.mod/module.cue +++ b/apps/dashboard/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/sdkkinds/dashboard" language: { - version: "v0.9.0" + version: "v0.11.0" } diff --git a/apps/example/kinds/cue.mod/module.cue b/apps/example/kinds/cue.mod/module.cue index b2f49fc6dc6..7720c73be54 100644 --- a/apps/example/kinds/cue.mod/module.cue +++ b/apps/example/kinds/cue.mod/module.cue @@ -1,2 +1,4 @@ module: "github.com/grafana/grafana/apps/example/kinds" -language: version: "v0.8.2" +language: { + version: "v0.11.0" +} diff --git a/apps/folder/Makefile b/apps/folder/Makefile index a6abc7a5337..f72a2bc7dbd 100644 --- a/apps/folder/Makefile +++ b/apps/folder/Makefile @@ -8,5 +8,6 @@ generate: install-app-sdk update-app-sdk ## Run Grafana App SDK code generation --grouping=group \ --defencoding=none \ --genoperatorstate=false \ - --noschemasinmanifest \ - --useoldmanifestkinds + --noschemasinmanifest + + \ No newline at end of file diff --git a/apps/folder/go.mod b/apps/folder/go.mod index 0e04414b8b8..c40b11f7add 100644 --- a/apps/folder/go.mod +++ b/apps/folder/go.mod @@ -10,60 +10,35 @@ require ( ) require ( - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/getkin/kin-openapi v0.133.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-openapi/jsonpointer v0.22.1 // indirect github.com/go-openapi/jsonreference v0.21.2 // indirect github.com/go-openapi/swag v0.23.1 // indirect github.com/go-openapi/swag/jsonname v0.25.1 // indirect - github.com/go-test/deep v1.1.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gnostic-models v0.7.0 // indirect - github.com/grafana/grafana-app-sdk/logging v0.48.3 // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect - github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect - github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_golang v1.23.2 // indirect - github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.67.3 // indirect - github.com/prometheus/procfs v0.16.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.11.1 // indirect - github.com/woodsbury/decimal128 v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/oauth2 v0.33.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect - golang.org/x/time v0.14.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/client-go v0.34.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/apps/folder/go.sum b/apps/folder/go.sum index c83ee8af102..286d9142c77 100644 --- a/apps/folder/go.sum +++ b/apps/folder/go.sum @@ -1,7 +1,3 @@ -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -10,8 +6,6 @@ github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bF github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ= -github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= @@ -22,8 +16,6 @@ github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZ github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU= github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo= -github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= -github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= @@ -31,33 +23,20 @@ github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7O github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -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/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= -github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= -github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:IA4SOwun8QyST9c5UNs/fN37XL6boXXDvRYFcFwbipg= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -66,29 +45,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= -github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= -github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= -github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= -github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= -github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= -github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.67.3 h1:shd26MlnwTw5jksTDhC7rTQIteBxy+ZZDr3t7F2xN2Q= -github.com/prometheus/common v0.67.3/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= @@ -99,20 +58,10 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0= -github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -128,24 +77,16 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= -golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -159,18 +100,12 @@ google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= -k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= -k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= diff --git a/apps/folder/kinds/cue.mod/module.cue b/apps/folder/kinds/cue.mod/module.cue index a127320c53d..cb78692c697 100644 --- a/apps/folder/kinds/cue.mod/module.cue +++ b/apps/folder/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/apps/folder/kinds" language: { - version: "v0.9.0" + version: "v0.11.0" } diff --git a/apps/folder/kinds/folder.cue b/apps/folder/kinds/folder.cue index 44423768205..24ba0c83dd4 100644 --- a/apps/folder/kinds/folder.cue +++ b/apps/folder/kinds/folder.cue @@ -1,26 +1,13 @@ package folder -folder: { +foldersV1beta1: { kind: "Folder" pluralName: "Folders" - current: "v1beta1" - versions: { - "v1beta1": { - codegen: { - ts: { - enabled: false // Not sure if it should be enabled or not, currently it is. - } - go: { - enabled: true - } - } - schema: { - spec: { - title: string - description?: string - } - status: {} // nothing - } + + schema: { + spec: { + title: string + description?: string } } -} +} \ No newline at end of file diff --git a/apps/folder/kinds/manifest.cue b/apps/folder/kinds/manifest.cue index cc8b8441315..42235710278 100644 --- a/apps/folder/kinds/manifest.cue +++ b/apps/folder/kinds/manifest.cue @@ -3,7 +3,15 @@ package folder manifest: { appName: "folder" groupOverride: "folder.grafana.app" - kinds: [ - folder, - ] -} + versions: { + "v1beta1": { + codegen: { + ts: {enabled: false} + go: {enabled: true} + } + kinds: [ + foldersV1beta1, + ] + } + } +} \ No newline at end of file diff --git a/apps/folder/pkg/apis/folder/v1beta1/folder_client_gen.go b/apps/folder/pkg/apis/folder/v1beta1/folder_client_gen.go index 6923c21d27c..501bf90a722 100644 --- a/apps/folder/pkg/apis/folder/v1beta1/folder_client_gen.go +++ b/apps/folder/pkg/apis/folder/v1beta1/folder_client_gen.go @@ -4,7 +4,6 @@ import ( "context" "github.com/grafana/grafana-app-sdk/resource" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type FolderClient struct { @@ -76,24 +75,6 @@ func (c *FolderClient) Patch(ctx context.Context, identifier resource.Identifier return c.client.Patch(ctx, identifier, req, opts) } -func (c *FolderClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus FolderStatus, opts resource.UpdateOptions) (*Folder, error) { - return c.client.Update(ctx, &Folder{ - TypeMeta: metav1.TypeMeta{ - Kind: FolderKind().Kind(), - APIVersion: GroupVersion.Identifier(), - }, - ObjectMeta: metav1.ObjectMeta{ - ResourceVersion: opts.ResourceVersion, - Namespace: identifier.Namespace, - Name: identifier.Name, - }, - Status: newStatus, - }, resource.UpdateOptions{ - Subresource: "status", - ResourceVersion: opts.ResourceVersion, - }) -} - func (c *FolderClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error { return c.client.Delete(ctx, identifier, opts) } diff --git a/apps/folder/pkg/apis/folder/v1beta1/folder_object_gen.go b/apps/folder/pkg/apis/folder/v1beta1/folder_object_gen.go index ad9318cb3d3..226af606f6f 100644 --- a/apps/folder/pkg/apis/folder/v1beta1/folder_object_gen.go +++ b/apps/folder/pkg/apis/folder/v1beta1/folder_object_gen.go @@ -21,8 +21,6 @@ type Folder struct { // Spec is the spec of the Folder Spec FolderSpec `json:"spec" yaml:"spec"` - - Status FolderStatus `json:"status" yaml:"status"` } func (o *Folder) GetSpec() any { @@ -39,15 +37,11 @@ func (o *Folder) SetSpec(spec any) error { } func (o *Folder) GetSubresources() map[string]any { - return map[string]any{ - "status": o.Status, - } + return map[string]any{} } func (o *Folder) GetSubresource(name string) (any, bool) { switch name { - case "status": - return o.Status, true default: return nil, false } @@ -55,13 +49,6 @@ func (o *Folder) GetSubresource(name string) (any, bool) { func (o *Folder) SetSubresource(name string, value any) error { switch name { - case "status": - cast, ok := value.(FolderStatus) - if !ok { - return fmt.Errorf("cannot set status type %#v, not of type FolderStatus", value) - } - o.Status = cast - return nil default: return fmt.Errorf("subresource '%s' does not exist", name) } @@ -233,7 +220,6 @@ func (o *Folder) DeepCopyInto(dst *Folder) { dst.TypeMeta.Kind = o.TypeMeta.Kind o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta) o.Spec.DeepCopyInto(&dst.Spec) - o.Status.DeepCopyInto(&dst.Status) } // Interface compliance compile-time check @@ -305,15 +291,3 @@ func (s *FolderSpec) DeepCopy() *FolderSpec { func (s *FolderSpec) DeepCopyInto(dst *FolderSpec) { resource.CopyObjectInto(dst, s) } - -// DeepCopy creates a full deep copy of FolderStatus -func (s *FolderStatus) DeepCopy() *FolderStatus { - cpy := &FolderStatus{} - s.DeepCopyInto(cpy) - return cpy -} - -// DeepCopyInto deep copies FolderStatus into another FolderStatus object -func (s *FolderStatus) DeepCopyInto(dst *FolderStatus) { - resource.CopyObjectInto(dst, s) -} diff --git a/apps/folder/pkg/apis/folder/v1beta1/folder_status_gen.go b/apps/folder/pkg/apis/folder/v1beta1/folder_status_gen.go deleted file mode 100644 index 1d1cd1f7d24..00000000000 --- a/apps/folder/pkg/apis/folder/v1beta1/folder_status_gen.go +++ /dev/null @@ -1,3 +0,0 @@ -// Code generated - EDITING IS FUTILE. DO NOT EDIT. - -package v1beta1 diff --git a/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi.go b/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi.go index 235987da8e2..5855d59c65a 100644 --- a/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi.go +++ b/apps/folder/pkg/apis/folder/v1beta1/zz_generated.openapi.go @@ -104,18 +104,12 @@ func schema_pkg_apis_folder_v1beta1_Folder(ref common.ReferenceCallback) common. Ref: ref("github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1.FolderSpec"), }, }, - "status": { - SchemaProps: spec.SchemaProps{ - Default: map[string]interface{}{}, - Ref: ref("github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1.FolderStatus"), - }, - }, }, - Required: []string{"metadata", "spec", "status"}, + Required: []string{"metadata", "spec"}, }, }, Dependencies: []string{ - "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1.FolderSpec", "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1.FolderStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, + "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1.FolderSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, } } diff --git a/apps/folder/pkg/apis/folder_manifest.go b/apps/folder/pkg/apis/folder_manifest.go deleted file mode 100644 index df164aa6ebc..00000000000 --- a/apps/folder/pkg/apis/folder_manifest.go +++ /dev/null @@ -1,116 +0,0 @@ -// -// This file is generated by grafana-app-sdk -// DO NOT EDIT -// - -package apis - -import ( - "fmt" - "strings" - - "github.com/grafana/grafana-app-sdk/app" - "github.com/grafana/grafana-app-sdk/resource" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/kube-openapi/pkg/spec3" - "k8s.io/kube-openapi/pkg/validation/spec" - - v1beta1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" -) - -var appManifestData = app.ManifestData{ - AppName: "folder", - Group: "folder.grafana.app", - PreferredVersion: "v1beta1", - Versions: []app.ManifestVersion{ - { - Name: "v1beta1", - Served: true, - Kinds: []app.ManifestVersionKind{ - { - Kind: "Folder", - Plural: "Folders", - Scope: "Namespaced", - Conversion: false, - }, - }, - Routes: app.ManifestVersionRoutes{ - Namespaced: map[string]spec3.PathProps{}, - Cluster: map[string]spec3.PathProps{}, - Schemas: map[string]spec.Schema{}, - }, - }, - }, -} - -func LocalManifest() app.Manifest { - return app.NewEmbeddedManifest(appManifestData) -} - -func RemoteManifest() app.Manifest { - return app.NewAPIServerManifest("folder") -} - -var kindVersionToGoType = map[string]resource.Kind{ - "Folder/v1beta1": v1beta1.FolderKind(), -} - -// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists. -// If there is no association for the provided Kind and Version, exists will return false. -func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) { - goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)] - return goType, exists -} - -var customRouteToGoResponseType = map[string]any{} - -// ManifestCustomRouteResponsesAssociator returns the associated response go type for a given kind, version, custom route path, and method, if one exists. -// kind may be empty for custom routes which are not kind subroutes. Leading slashes are removed from subroute paths. -// If there is no association for the provided kind, version, custom route path, and method, exists will return false. -// Resource routes (those without a kind) should prefix their route with "/" if the route is namespaced (otherwise the route is assumed to be cluster-scope) -func ManifestCustomRouteResponsesAssociator(kind, version, path, verb string) (goType any, exists bool) { - if len(path) > 0 && path[0] == '/' { - path = path[1:] - } - goType, exists = customRouteToGoResponseType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))] - return goType, exists -} - -var customRouteToGoParamsType = map[string]runtime.Object{} - -func ManifestCustomRouteQueryAssociator(kind, version, path, verb string) (goType runtime.Object, exists bool) { - if len(path) > 0 && path[0] == '/' { - path = path[1:] - } - goType, exists = customRouteToGoParamsType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))] - return goType, exists -} - -var customRouteToGoRequestBodyType = map[string]any{} - -func ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb string) (goType any, exists bool) { - if len(path) > 0 && path[0] == '/' { - path = path[1:] - } - goType, exists = customRouteToGoRequestBodyType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))] - return goType, exists -} - -type GoTypeAssociator struct{} - -func NewGoTypeAssociator() *GoTypeAssociator { - return &GoTypeAssociator{} -} - -func (g *GoTypeAssociator) KindToGoType(kind, version string) (goType resource.Kind, exists bool) { - return ManifestGoTypeAssociator(kind, version) -} -func (g *GoTypeAssociator) CustomRouteReturnGoType(kind, version, path, verb string) (goType any, exists bool) { - return ManifestCustomRouteResponsesAssociator(kind, version, path, verb) -} -func (g *GoTypeAssociator) CustomRouteQueryGoType(kind, version, path, verb string) (goType runtime.Object, exists bool) { - return ManifestCustomRouteQueryAssociator(kind, version, path, verb) -} -func (g *GoTypeAssociator) CustomRouteRequestBodyGoType(kind, version, path, verb string) (goType any, exists bool) { - return ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb) -} diff --git a/apps/iam/kinds/cue.mod/module.cue b/apps/iam/kinds/cue.mod/module.cue index d1ddb24fcea..7ca193c8ffd 100644 --- a/apps/iam/kinds/cue.mod/module.cue +++ b/apps/iam/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/apps/iam/kinds" language: { - version: "v0.9.0" + version: "v0.11.0" } diff --git a/apps/investigations/kinds/cue.mod/module.cue b/apps/investigations/kinds/cue.mod/module.cue index 1c9c98f497f..c592b1ce788 100644 --- a/apps/investigations/kinds/cue.mod/module.cue +++ b/apps/investigations/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/apps/investigations" language: { - version: "v0.9.0" + version: "v0.11.0" } \ No newline at end of file diff --git a/apps/logsdrilldown/kinds/cue.mod/module.cue b/apps/logsdrilldown/kinds/cue.mod/module.cue index 641554da67c..354ce7e3290 100644 --- a/apps/logsdrilldown/kinds/cue.mod/module.cue +++ b/apps/logsdrilldown/kinds/cue.mod/module.cue @@ -1,2 +1,4 @@ module: "github.com/grafana/grafana/apps/logsdrilldown/kinds" -language: version: "v0.8.2" +language: { + version: "v0.11.0" +} diff --git a/apps/playlist/kinds/cue.mod/module.cue b/apps/playlist/kinds/cue.mod/module.cue index 162ee6176aa..ee4b35ffdf8 100644 --- a/apps/playlist/kinds/cue.mod/module.cue +++ b/apps/playlist/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/apps/playlist/kinds" language: { - version: "v0.9.0" + version: "v0.11.0" } diff --git a/apps/plugins/kinds/cue.mod/module.cue b/apps/plugins/kinds/cue.mod/module.cue index 95dc1c62455..8cb2daac461 100644 --- a/apps/plugins/kinds/cue.mod/module.cue +++ b/apps/plugins/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/apps/plugins/kinds" language: { - version: "v0.9.0" + version: "v0.11.0" } diff --git a/apps/preferences/kinds/cue.mod/module.cue b/apps/preferences/kinds/cue.mod/module.cue index 6280fd8f227..9675fc32a51 100644 --- a/apps/preferences/kinds/cue.mod/module.cue +++ b/apps/preferences/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/apps/preferences/kinds" language: { - version: "v0.9.0" + version: "v0.11.0" } diff --git a/apps/provisioning/kinds/cue.mod/module.cue b/apps/provisioning/kinds/cue.mod/module.cue index 20a2c78795d..421207ff553 100644 --- a/apps/provisioning/kinds/cue.mod/module.cue +++ b/apps/provisioning/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/apps/provisioning" language: { - version: "v0.9.0" + version: "v0.11.0" } \ No newline at end of file diff --git a/apps/secret/kinds/cue.mod/module.cue b/apps/secret/kinds/cue.mod/module.cue index bad5ef304e1..d293a6b30e1 100644 --- a/apps/secret/kinds/cue.mod/module.cue +++ b/apps/secret/kinds/cue.mod/module.cue @@ -1,4 +1,4 @@ module: "github.com/grafana/grafana/apps/secret/kinds" language: { - version: "v0.9.0" + version: "v0.11.0" } diff --git a/apps/shorturl/kinds/cue.mod/module.cue b/apps/shorturl/kinds/cue.mod/module.cue index 2e6cdd1ee5b..f4ce8b944f2 100644 --- a/apps/shorturl/kinds/cue.mod/module.cue +++ b/apps/shorturl/kinds/cue.mod/module.cue @@ -1,2 +1,4 @@ module: "github.com/grafana/grafana/apps/shorturl/kinds" -language: version: "v0.8.2" +language: { + version: "v0.11.0" +} diff --git a/packages/grafana-api-clients/src/clients/rtkq/folder/v1beta1/endpoints.gen.ts b/packages/grafana-api-clients/src/clients/rtkq/folder/v1beta1/endpoints.gen.ts index 984d95dcd68..7af6983ea11 100644 --- a/packages/grafana-api-clients/src/clients/rtkq/folder/v1beta1/endpoints.gen.ts +++ b/packages/grafana-api-clients/src/clients/rtkq/folder/v1beta1/endpoints.gen.ts @@ -444,7 +444,6 @@ export type FolderSpec = { description?: string; title: string; }; -export type FolderStatus = object; export type Folder = { /** 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; @@ -453,7 +452,6 @@ export type Folder = { metadata: ObjectMeta; /** Spec is the spec of the Folder */ spec: FolderSpec; - status: FolderStatus; }; 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. */ diff --git a/pkg/tests/apis/folder/folders_test.go b/pkg/tests/apis/folder/folders_test.go index e692b991ffb..43db6ff0932 100644 --- a/pkg/tests/apis/folder/folders_test.go +++ b/pkg/tests/apis/folder/folders_test.go @@ -14,7 +14,6 @@ import ( "time" "github.com/google/uuid" - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" "github.com/prometheus/common/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -23,6 +22,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/apimachinery/utils" @@ -357,9 +357,7 @@ func doFolderTests(t *testing.T, helper *apis.K8sTestHelper) *apis.K8sTestHelper "spec": { "title": "Test", "description": "" - }, - "status": {} - }` + }}` // Get should return the same result found, err := client.Resource.Get(context.Background(), uid, metav1.GetOptions{}) diff --git a/pkg/tests/apis/openapi_snapshots/folder.grafana.app-v1beta1.json b/pkg/tests/apis/openapi_snapshots/folder.grafana.app-v1beta1.json index f366842cb4d..72db9d6bb80 100644 --- a/pkg/tests/apis/openapi_snapshots/folder.grafana.app-v1beta1.json +++ b/pkg/tests/apis/openapi_snapshots/folder.grafana.app-v1beta1.json @@ -1103,8 +1103,7 @@ "type": "object", "required": [ "metadata", - "spec", - "status" + "spec" ], "properties": { "apiVersion": { @@ -1131,14 +1130,6 @@ "$ref": "#/components/schemas/com.github.grafana.grafana.apps.folder.pkg.apis.folder.v1beta1.FolderSpec" } ] - }, - "status": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.folder.pkg.apis.folder.v1beta1.FolderStatus" - } - ] } }, "x-kubernetes-group-version-kind": [ @@ -1329,10 +1320,6 @@ } } }, - "com.github.grafana.grafana.apps.folder.pkg.apis.folder.v1beta1.FolderStatus": { - "description": "Empty stub", - "type": "object" - }, "com.github.grafana.grafana.apps.folder.pkg.apis.folder.v1beta1.ResourceStats": { "type": "object", "required": [ diff --git a/public/app/api/clients/folder/v1beta1/hooks.ts b/public/app/api/clients/folder/v1beta1/hooks.ts index 1fea62c40d0..e33da05defa 100644 --- a/public/app/api/clients/folder/v1beta1/hooks.ts +++ b/public/app/api/clients/folder/v1beta1/hooks.ts @@ -371,7 +371,6 @@ export function useCreateFolder() { ...(folder.parentUid && { [AnnoKeyFolder]: folder.parentUid }), }, }, - status: {}, }, }; @@ -405,7 +404,6 @@ export function useUpdateFolder() { metadata: { name: folder.uid, }, - status: {}, }, }; diff --git a/public/app/features/provisioning/components/Folders/NewProvisionedFolderForm.test.tsx b/public/app/features/provisioning/components/Folders/NewProvisionedFolderForm.test.tsx index 1aa011c708c..3dcd2358ce2 100644 --- a/public/app/features/provisioning/components/Folders/NewProvisionedFolderForm.test.tsx +++ b/public/app/features/provisioning/components/Folders/NewProvisionedFolderForm.test.tsx @@ -154,7 +154,6 @@ const mockHookData: ProvisionedFolderFormDataResult = { spec: { title: '', }, - status: {}, }, workflowOptions: [ { label: 'Commit directly', value: 'write' }, From b719aea078b13cf0ff222eb138a0b81c921e2829 Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Fri, 5 Dec 2025 13:25:56 +0100 Subject: [PATCH 21/48] Azure: Fix `dcount` aggregation (#114666) * Add parameter type field * Use parameterType to filter columns for aggregation funcs * Support selecting column for dcount aggregation --- .../azuremonitor/components/LogsQueryBuilder/AggregateItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregateItem.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregateItem.tsx index 2eacf01b8bd..294c85fdb87 100644 --- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregateItem.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryBuilder/AggregateItem.tsx @@ -30,7 +30,7 @@ const AggregateItem: React.FC = ({ templateVariableOptions, }) => { const isPercentile = aggregate.reduce?.name === 'percentile'; - const isCountAggregate = aggregate.reduce?.name?.includes('count'); + const isCountAggregate = aggregate.reduce?.name === 'count'; const [percentileValue, setPercentileValue] = useState(aggregate.parameters?.[0]?.value || ''); const [columnValue, setColumnValue] = useState( From 65817794b5d8f7186d4b9d911618978df8091362 Mon Sep 17 00:00:00 2001 From: Tung Nguyen Date: Fri, 5 Dec 2025 14:26:56 +0200 Subject: [PATCH 22/48] OpenTSDB: Remove gf-form from opentsdb Annotation Editor (#112590) * Chore: Remove gf-form in opentsdb AnnotationEditor * Fix: small typo * chore: remove stale eslint suppression rule --- eslint-suppressions.json | 7 +------ .../opentsdb/components/AnnotationEditor.tsx | 14 +++++++------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 492d5e18958..f90a21cb7c5 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -4105,11 +4105,6 @@ "count": 10 } }, - "public/app/plugins/datasource/opentsdb/components/AnnotationEditor.tsx": { - "no-restricted-syntax": { - "count": 3 - } - }, "public/app/plugins/datasource/opentsdb/components/OpenTsdbDetails.tsx": { "no-restricted-syntax": { "count": 3 @@ -4682,4 +4677,4 @@ "count": 1 } } -} \ No newline at end of file +} diff --git a/public/app/plugins/datasource/opentsdb/components/AnnotationEditor.tsx b/public/app/plugins/datasource/opentsdb/components/AnnotationEditor.tsx index d6fbfd8b8c4..e3e579185db 100644 --- a/public/app/plugins/datasource/opentsdb/components/AnnotationEditor.tsx +++ b/public/app/plugins/datasource/opentsdb/components/AnnotationEditor.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { QueryEditorProps } from '@grafana/data'; -import { InlineFormLabel, Input, InlineSwitch } from '@grafana/ui'; +import { InlineFormLabel, Input, InlineSwitch, Stack } from '@grafana/ui'; import OpenTsDatasource from '../datasource'; import { OpenTsdbQuery, OpenTsdbOptions } from '../types'; @@ -26,8 +26,8 @@ export const AnnotationEditor = (props: QueryEditorProps -
    + + OpenTSDB metrics query updateValue('target', target)} placeholder="events.eventname" /> -
    -
    + + Show Global Annotations? updateIsGlobal(isGlobal)} /> -
    -
  • +
    +
    ); }; From 8a0fa93aecd8e85c82305b834b726572102ab691 Mon Sep 17 00:00:00 2001 From: Alexander Zobnin Date: Fri, 5 Dec 2025 13:55:56 +0100 Subject: [PATCH 23/48] Zanzana: Fix duplicated writes in one request (#114900) * Zanzana: Fix duplicated writes * add tests --- .../authz/zanzana/server/server_mutate.go | 66 ++++++++++++++++++- .../zanzana/server/server_mutate_folder.go | 19 +----- .../zanzana/server/server_mutate_org_role.go | 19 +----- .../server_mutate_resourcepermissions.go | 19 +----- .../server/server_mutate_rolebindings.go | 19 +----- .../zanzana/server/server_mutate_roles.go | 19 +----- .../server/server_mutate_teambindings.go | 19 +----- .../zanzana/server/server_mutate_test.go | 64 ++++++++++++++++++ 8 files changed, 135 insertions(+), 109 deletions(-) diff --git a/pkg/services/authz/zanzana/server/server_mutate.go b/pkg/services/authz/zanzana/server/server_mutate.go index bd339534fe5..15a57404bbc 100644 --- a/pkg/services/authz/zanzana/server/server_mutate.go +++ b/pkg/services/authz/zanzana/server/server_mutate.go @@ -6,8 +6,10 @@ import ( "fmt" "time" - authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1" + openfgav1 "github.com/openfga/api/proto/openfga/v1" "go.opentelemetry.io/otel/codes" + + authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1" ) type OperationGroup string @@ -119,3 +121,65 @@ func groupByOperation(operations []*authzextv1.MutateOperation) (map[OperationGr return grouped, nil } + +func deduplicateTupleKeys(writeTuples []*openfgav1.TupleKey, deleteTuples []*openfgav1.TupleKeyWithoutCondition) ([]*openfgav1.TupleKey, []*openfgav1.TupleKeyWithoutCondition) { + deduplicatedWriteTuples := make([]*openfgav1.TupleKey, 0) + deduplicatedDeleteTuples := make([]*openfgav1.TupleKeyWithoutCondition, 0) + + writeTupleMap := make(map[string]bool) + + for _, writeTuple := range writeTuples { + id := getTupleKeyID(writeTuple) + if !writeTupleMap[id] { + writeTupleMap[id] = true + deduplicatedWriteTuples = append(deduplicatedWriteTuples, writeTuple) + } + } + + // Prioritize writes over deletes. Deletes do not have a condition, so we don't know if write tuple is different from delete one. + for _, deleteTuple := range deleteTuples { + id := getTupleKeyID(deleteTuple) + if !writeTupleMap[id] { + writeTupleMap[id] = true + deduplicatedDeleteTuples = append(deduplicatedDeleteTuples, deleteTuple) + } + } + + return deduplicatedWriteTuples, deduplicatedDeleteTuples +} + +func (s *Server) writeTuples(ctx context.Context, store *storeInfo, writeTuples []*openfgav1.TupleKey, deleteTuples []*openfgav1.TupleKeyWithoutCondition) error { + writeReq := &openfgav1.WriteRequest{ + StoreId: store.ID, + AuthorizationModelId: store.ModelID, + } + + writeTuples, deleteTuples = deduplicateTupleKeys(writeTuples, deleteTuples) + + if len(writeTuples) > 0 { + writeReq.Writes = &openfgav1.WriteRequestWrites{ + TupleKeys: writeTuples, + OnDuplicate: "ignore", + } + } + + if len(deleteTuples) > 0 { + writeReq.Deletes = &openfgav1.WriteRequestDeletes{ + TupleKeys: deleteTuples, + OnMissing: "ignore", + } + } + + _, err := s.openfga.Write(ctx, writeReq) + return err +} + +type TupleKey interface { + GetUser() string + GetRelation() string + GetObject() string +} + +func getTupleKeyID(t TupleKey) string { + return fmt.Sprintf("%s:%s:%s", t.GetUser(), t.GetRelation(), t.GetObject()) +} diff --git a/pkg/services/authz/zanzana/server/server_mutate_folder.go b/pkg/services/authz/zanzana/server/server_mutate_folder.go index 3d92347f404..6d07492b788 100644 --- a/pkg/services/authz/zanzana/server/server_mutate_folder.go +++ b/pkg/services/authz/zanzana/server/server_mutate_folder.go @@ -52,24 +52,7 @@ func (s *Server) mutateFolders(ctx context.Context, store *storeInfo, operations return nil } - writeReq := &openfgav1.WriteRequest{ - StoreId: store.ID, - AuthorizationModelId: store.ModelID, - } - if len(writeTuples) > 0 { - writeReq.Writes = &openfgav1.WriteRequestWrites{ - TupleKeys: writeTuples, - OnDuplicate: "ignore", - } - } - if len(deleteTuples) > 0 { - writeReq.Deletes = &openfgav1.WriteRequestDeletes{ - TupleKeys: deleteTuples, - OnMissing: "ignore", - } - } - - _, err := s.openfga.Write(ctx, writeReq) + err := s.writeTuples(ctx, store, writeTuples, deleteTuples) if err != nil { s.logger.Error("failed to write folder tuples", "error", err) return err diff --git a/pkg/services/authz/zanzana/server/server_mutate_org_role.go b/pkg/services/authz/zanzana/server/server_mutate_org_role.go index bda9decb3d5..843c62859dd 100644 --- a/pkg/services/authz/zanzana/server/server_mutate_org_role.go +++ b/pkg/services/authz/zanzana/server/server_mutate_org_role.go @@ -50,24 +50,7 @@ func (s *Server) mutateOrgRoles(ctx context.Context, store *storeInfo, operation return nil } - writeReq := &openfgav1.WriteRequest{ - StoreId: store.ID, - AuthorizationModelId: store.ModelID, - } - if len(writeTuples) > 0 { - writeReq.Writes = &openfgav1.WriteRequestWrites{ - TupleKeys: writeTuples, - OnDuplicate: "ignore", - } - } - if len(deleteTuples) > 0 { - writeReq.Deletes = &openfgav1.WriteRequestDeletes{ - TupleKeys: deleteTuples, - OnMissing: "ignore", - } - } - - _, err := s.openfga.Write(ctx, writeReq) + err := s.writeTuples(ctx, store, writeTuples, deleteTuples) if err != nil { s.logger.Error("failed to write user org role tuples", "error", err) return err diff --git a/pkg/services/authz/zanzana/server/server_mutate_resourcepermissions.go b/pkg/services/authz/zanzana/server/server_mutate_resourcepermissions.go index fa8b5467235..f85f31900f6 100644 --- a/pkg/services/authz/zanzana/server/server_mutate_resourcepermissions.go +++ b/pkg/services/authz/zanzana/server/server_mutate_resourcepermissions.go @@ -47,24 +47,7 @@ func (s *Server) mutateResourcePermissions(ctx context.Context, store *storeInfo } } - writeReq := &openfgav1.WriteRequest{ - StoreId: store.ID, - AuthorizationModelId: store.ModelID, - } - if len(writeTuples) > 0 { - writeReq.Writes = &openfgav1.WriteRequestWrites{ - TupleKeys: writeTuples, - OnDuplicate: "ignore", - } - } - if len(deleteTuples) > 0 { - writeReq.Deletes = &openfgav1.WriteRequestDeletes{ - TupleKeys: deleteTuples, - OnMissing: "ignore", - } - } - - _, err := s.openfga.Write(ctx, writeReq) + err := s.writeTuples(ctx, store, writeTuples, deleteTuples) if err != nil { s.logger.Error("failed to write resource permission tuples", "error", err) return err diff --git a/pkg/services/authz/zanzana/server/server_mutate_rolebindings.go b/pkg/services/authz/zanzana/server/server_mutate_rolebindings.go index 3b18566bee2..faf23d1f1ed 100644 --- a/pkg/services/authz/zanzana/server/server_mutate_rolebindings.go +++ b/pkg/services/authz/zanzana/server/server_mutate_rolebindings.go @@ -44,24 +44,7 @@ func (s *Server) mutateRoleBindings(ctx context.Context, store *storeInfo, opera } } - writeReq := &openfgav1.WriteRequest{ - StoreId: store.ID, - AuthorizationModelId: store.ModelID, - } - if len(writeTuples) > 0 { - writeReq.Writes = &openfgav1.WriteRequestWrites{ - TupleKeys: writeTuples, - OnDuplicate: "ignore", - } - } - if len(deleteTuples) > 0 { - writeReq.Deletes = &openfgav1.WriteRequestDeletes{ - TupleKeys: deleteTuples, - OnMissing: "ignore", - } - } - - _, err := s.openfga.Write(ctx, writeReq) + err := s.writeTuples(ctx, store, writeTuples, deleteTuples) if err != nil { s.logger.Error("failed to write resource role binding tuples", "error", err) return err diff --git a/pkg/services/authz/zanzana/server/server_mutate_roles.go b/pkg/services/authz/zanzana/server/server_mutate_roles.go index 4c19b1fd288..c0471fdbf17 100644 --- a/pkg/services/authz/zanzana/server/server_mutate_roles.go +++ b/pkg/services/authz/zanzana/server/server_mutate_roles.go @@ -41,24 +41,7 @@ func (s *Server) mutateRoles(ctx context.Context, store *storeInfo, operations [ } } - writeReq := &openfgav1.WriteRequest{ - StoreId: store.ID, - AuthorizationModelId: store.ModelID, - } - if len(writeTuples) > 0 { - writeReq.Writes = &openfgav1.WriteRequestWrites{ - TupleKeys: writeTuples, - OnDuplicate: "ignore", - } - } - if len(deleteTuples) > 0 { - writeReq.Deletes = &openfgav1.WriteRequestDeletes{ - TupleKeys: deleteTuples, - OnMissing: "ignore", - } - } - - _, err := s.openfga.Write(ctx, writeReq) + err := s.writeTuples(ctx, store, writeTuples, deleteTuples) if err != nil { s.logger.Error("failed to write resource role binding tuples", "error", err) return err diff --git a/pkg/services/authz/zanzana/server/server_mutate_teambindings.go b/pkg/services/authz/zanzana/server/server_mutate_teambindings.go index 81e1c9cb437..96690bb96d8 100644 --- a/pkg/services/authz/zanzana/server/server_mutate_teambindings.go +++ b/pkg/services/authz/zanzana/server/server_mutate_teambindings.go @@ -43,24 +43,7 @@ func (s *Server) mutateTeamBindings(ctx context.Context, store *storeInfo, opera } } - writeReq := &openfgav1.WriteRequest{ - StoreId: store.ID, - AuthorizationModelId: store.ModelID, - } - if len(writeTuples) > 0 { - writeReq.Writes = &openfgav1.WriteRequestWrites{ - TupleKeys: writeTuples, - OnDuplicate: "ignore", - } - } - if len(deleteTuples) > 0 { - writeReq.Deletes = &openfgav1.WriteRequestDeletes{ - TupleKeys: deleteTuples, - OnMissing: "ignore", - } - } - - _, err := s.openfga.Write(ctx, writeReq) + err := s.writeTuples(ctx, store, writeTuples, deleteTuples) if err != nil { s.logger.Error("failed to write resource role binding tuples", "error", err) return err diff --git a/pkg/services/authz/zanzana/server/server_mutate_test.go b/pkg/services/authz/zanzana/server/server_mutate_test.go index 70dc1ea2fb8..c1fcfabbe43 100644 --- a/pkg/services/authz/zanzana/server/server_mutate_test.go +++ b/pkg/services/authz/zanzana/server/server_mutate_test.go @@ -5,6 +5,7 @@ import ( openfgav1 "github.com/openfga/api/proto/openfga/v1" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/structpb" iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1" v1 "github.com/grafana/grafana/pkg/services/authz/proto/v1" @@ -133,3 +134,66 @@ func testMutate(t *testing.T, srv *Server) { require.Len(t, res.Tuples, 0) }) } + +func TestDeduplicateTupleKeys(t *testing.T) { + t.Run("should deduplicate write tuples", func(t *testing.T) { + writeTuples := []*openfgav1.TupleKey{ + {User: "user:1", Relation: "get", Object: "object:1"}, + {User: "user:1", Relation: "get", Object: "object:2"}, + } + deleteTuples := []*openfgav1.TupleKeyWithoutCondition{ + {User: "user:1", Relation: "get", Object: "object:1"}, + {User: "user:2", Relation: "get", Object: "object:2"}, + } + + deduplicatedWriteTuples, deduplicatedDeleteTuples := deduplicateTupleKeys(writeTuples, deleteTuples) + require.Len(t, deduplicatedWriteTuples, 2) + require.ElementsMatch(t, deduplicatedWriteTuples, []*openfgav1.TupleKey{ + {User: "user:1", Relation: "get", Object: "object:1"}, + {User: "user:1", Relation: "get", Object: "object:2"}, + }) + + require.Len(t, deduplicatedDeleteTuples, 1) + require.ElementsMatch(t, deduplicatedDeleteTuples, []*openfgav1.TupleKeyWithoutCondition{ + {User: "user:2", Relation: "get", Object: "object:2"}, + }) + }) + + t.Run("should deduplicate write tuples with conditions", func(t *testing.T) { + writeTuples := []*openfgav1.TupleKey{ + {User: "user:1", Relation: "get", Object: "object:1", Condition: &openfgav1.RelationshipCondition{Name: "condition:1", Context: &structpb.Struct{Fields: map[string]*structpb.Value{ + "field:1": structpb.NewStringValue("value:1"), + }}}}, + {User: "user:1", Relation: "get", Object: "object:2"}, + } + deleteTuples := []*openfgav1.TupleKeyWithoutCondition{ + {User: "user:1", Relation: "get", Object: "object:1"}, + } + + deduplicatedWriteTuples, deduplicatedDeleteTuples := deduplicateTupleKeys(writeTuples, deleteTuples) + require.Len(t, deduplicatedWriteTuples, 2) + require.ElementsMatch(t, deduplicatedWriteTuples, []*openfgav1.TupleKey{ + {User: "user:1", Relation: "get", Object: "object:1", Condition: &openfgav1.RelationshipCondition{Name: "condition:1", Context: &structpb.Struct{Fields: map[string]*structpb.Value{ + "field:1": structpb.NewStringValue("value:1"), + }}}}, + {User: "user:1", Relation: "get", Object: "object:2"}, + }) + + require.Len(t, deduplicatedDeleteTuples, 0) + }) + + t.Run("should do nothing for no duplicates", func(t *testing.T) { + writeTuples := []*openfgav1.TupleKey{ + {User: "user:1", Relation: "get", Object: "object:1"}, + } + deleteTuples := []*openfgav1.TupleKeyWithoutCondition{ + {User: "user:2", Relation: "get", Object: "object:2"}, + } + + deduplicatedWriteTuples, deduplicatedDeleteTuples := deduplicateTupleKeys(writeTuples, deleteTuples) + require.Len(t, deduplicatedWriteTuples, 1) + require.ElementsMatch(t, deduplicatedWriteTuples, writeTuples) + require.Len(t, deduplicatedDeleteTuples, 1) + require.ElementsMatch(t, deduplicatedDeleteTuples, deleteTuples) + }) +} From 5ac702a4c16b17ac08f067622ffe98ca4d855f1b Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Fri, 5 Dec 2025 17:21:30 +0300 Subject: [PATCH 24/48] Dashboards: update manifest to avoid useoldmanifestkinds (#114910) --- apps/dashboard/Makefile | 3 +- apps/dashboard/kinds/dashboard.cue | 54 ------------ apps/dashboard/kinds/manifest.cue | 84 +++++++++++++++++-- apps/dashboard/kinds/snapshot.cue | 62 +++++--------- apps/dashboard/pkg/apis/dashboard/utils.go | 2 + .../conversion/v1beta1_to_v2alpha1.go | 4 +- .../conversion/v1beta1_to_v2alpha1_test.go | 4 +- pkg/tsdb/grafanads/grafana.go | 3 +- 8 files changed, 110 insertions(+), 106 deletions(-) diff --git a/apps/dashboard/Makefile b/apps/dashboard/Makefile index fa75513d964..3d5c7060199 100644 --- a/apps/dashboard/Makefile +++ b/apps/dashboard/Makefile @@ -12,8 +12,7 @@ do-generate: install-app-sdk update-app-sdk ## Run Grafana App SDK code generati --grouping=group \ --defencoding=none \ --genoperatorstate=false \ - --noschemasinmanifest \ - --useoldmanifestkinds + --noschemasinmanifest .PHONY: post-generate-cleanup post-generate-cleanup: ## Clean up the generated code diff --git a/apps/dashboard/kinds/dashboard.cue b/apps/dashboard/kinds/dashboard.cue index e8dfea3bf98..5d04cf1e331 100644 --- a/apps/dashboard/kinds/dashboard.cue +++ b/apps/dashboard/kinds/dashboard.cue @@ -1,12 +1,5 @@ package kinds -import ( - v0 "github.com/grafana/grafana/sdkkinds/dashboard/v0alpha1" - v1 "github.com/grafana/grafana/sdkkinds/dashboard/v1beta1" - v2alpha1 "github.com/grafana/grafana/sdkkinds/dashboard/v2alpha1" - v2beta1 "github.com/grafana/grafana/sdkkinds/dashboard/v2beta1" -) - // Status is the shared status of all dashboard versions. DashboardStatus: { // Optional conversion status. @@ -31,50 +24,3 @@ ConversionStatus: { // The original value map[string]any source?: _ } - -dashboard: { - kind: "Dashboard" - pluralName: "Dashboards" - current: "v1beta1" - codegen: { - ts: { - enabled: true - config: { - enumsAsUnionTypes: true - } - } - go: { - enabled: true - config: { - allowMarshalEmptyDisjunctions: true - } - } - } - - versions: { - "v0alpha1": { - schema: { - spec: v0.DashboardSpec - status: DashboardStatus - } - } - "v1beta1": { - schema: { - spec: v1.DashboardSpec - status: DashboardStatus - } - } - "v2alpha1": { - schema: { - spec: v2alpha1.DashboardSpec - status: DashboardStatus - } - } - "v2beta1": { - schema: { - spec: v2beta1.DashboardSpec - status: DashboardStatus - } - } - } -} diff --git a/apps/dashboard/kinds/manifest.cue b/apps/dashboard/kinds/manifest.cue index f1044a39e24..9fb17910664 100644 --- a/apps/dashboard/kinds/manifest.cue +++ b/apps/dashboard/kinds/manifest.cue @@ -1,10 +1,82 @@ package kinds +import ( + v0 "github.com/grafana/grafana/sdkkinds/dashboard/v0alpha1" + v1 "github.com/grafana/grafana/sdkkinds/dashboard/v1beta1" + v2alpha1 "github.com/grafana/grafana/sdkkinds/dashboard/v2alpha1" + v2beta1 "github.com/grafana/grafana/sdkkinds/dashboard/v2beta1" +) + manifest: { - appName: "dashboard" - groupOverride: "dashboard.grafana.app" - kinds: [ - dashboard, - snapshot, - ] + appName: "dashboard" + groupOverride: "dashboard.grafana.app" + preferredVersion: "v1beta1" + + versions: { + "v0alpha1": { + codegen: { + ts: {enabled: false} + go: {enabled: true} + } + kinds: [ + { + kind: "Dashboard" + pluralName: "Dashboards" + schema: { + spec: v0.DashboardSpec + status: DashboardStatus + } + }, + snapshotV0alpha1, // Only exists in v0alpha (for now) + ] + } + "v1beta1": { + codegen: { + ts: {enabled: false} + go: {enabled: true} + } + kinds: [ + { + kind: "Dashboard" + pluralName: "Dashboards" + schema: { + spec: v1.DashboardSpec + status: DashboardStatus + } + } + ] + } + "v2alpha1": { + codegen: { + ts: {enabled: false} + go: {enabled: true} + } + kinds: [ + { + kind: "Dashboard" + pluralName: "Dashboards" + schema: { + spec: v2alpha1.DashboardSpec + status: DashboardStatus + } + } + ] + } + "v2beta1": { + codegen: { + ts: {enabled: false} + go: {enabled: true} + } + kinds: [ + { + kind: "Dashboard" + pluralName: "Dashboards" + schema: { + spec: v2beta1.DashboardSpec + status: DashboardStatus + } + } + ] + } + } } diff --git a/apps/dashboard/kinds/snapshot.cue b/apps/dashboard/kinds/snapshot.cue index c224daf8492..00f445881b5 100644 --- a/apps/dashboard/kinds/snapshot.cue +++ b/apps/dashboard/kinds/snapshot.cue @@ -1,46 +1,30 @@ package kinds -snapshot: { +snapshotV0alpha1: { kind: "Snapshot" pluralName: "Snapshots" - scope: "Namespaced" - current: "v0alpha1" - - codegen: { - ts: { - enabled: true - } - go: { - enabled: true - } - } - - versions: { - "v0alpha1": { - schema: { - spec: { - // Snapshot title - title?: string - - // Optionally auto-remove the snapshot at a future date (Unix timestamp in seconds) - expires?: int64 | *0 - - // When set to true, the snapshot exists in a remote server - external?: bool | *false - - // The external URL where the snapshot can be seen - externalUrl?: string - - // The URL that created the dashboard originally - originalUrl?: string - - // Snapshot creation timestamp - timestamp?: string + schema: { + spec: { + // Snapshot title + title?: string + + // Optionally auto-remove the snapshot at a future date (Unix timestamp in seconds) + expires?: int64 | *0 + + // When set to true, the snapshot exists in a remote server + external?: bool | *false + + // The external URL where the snapshot can be seen + externalUrl?: string + + // The URL that created the dashboard originally + originalUrl?: string + + // Snapshot creation timestamp + timestamp?: string - // The raw dashboard (unstructured for now) - dashboard?: [string]: _ - } - } + // The raw dashboard (unstructured for now) + dashboard?: [string]: _ } } -} +} \ No newline at end of file diff --git a/apps/dashboard/pkg/apis/dashboard/utils.go b/apps/dashboard/pkg/apis/dashboard/utils.go index a5979151a90..f1453d548a0 100644 --- a/apps/dashboard/pkg/apis/dashboard/utils.go +++ b/apps/dashboard/pkg/apis/dashboard/utils.go @@ -6,6 +6,8 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/utils" ) +const GrafanaDatasourceUID = "grafana" + // SetPluginIDMeta sets the repo name to "plugin" and the path to the plugin ID func SetPluginIDMeta(obj *unstructured.Unstructured, pluginID string) { if pluginID == "" { diff --git a/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1.go b/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1.go index e6599b1d8eb..231c0ad4131 100644 --- a/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1.go +++ b/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1.go @@ -12,11 +12,11 @@ import ( "k8s.io/apiserver/pkg/endpoints/request" "github.com/grafana/authlib/types" + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard" dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1" dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1" schemaversion "github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" "github.com/grafana/grafana/pkg/apimachinery/identity" - "github.com/grafana/grafana/pkg/tsdb/grafanads" ) // getDefaultDatasourceType gets the default datasource type using the datasource provider @@ -58,7 +58,7 @@ func getDatasourceTypeByUID(ctx context.Context, uid string, provider schemavers // datasource: { type: "datasource" } with no UID, it should resolve to uid: "grafana". func resolveGrafanaDatasourceUID(dsType, dsUID string) string { if dsType == "datasource" && dsUID == "" { - return grafanads.DatasourceUID + return dashboard.GrafanaDatasourceUID } return dsUID } diff --git a/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1_test.go b/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1_test.go index 6bbdf1ca214..3dad9188fe7 100644 --- a/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1_test.go +++ b/apps/dashboard/pkg/migration/conversion/v1beta1_to_v2alpha1_test.go @@ -7,11 +7,11 @@ import ( "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime" + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard" dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1" dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1" "github.com/grafana/grafana/apps/dashboard/pkg/migration" migrationtestutil "github.com/grafana/grafana/apps/dashboard/pkg/migration/testutil" - "github.com/grafana/grafana/pkg/tsdb/grafanads" ) // TestV1beta1ToV2alpha1 tests conversion from v1beta1 to v2alpha1 with various datasource scenarios @@ -77,7 +77,7 @@ func TestV1beta1ToV2alpha1(t *testing.T) { // Verify datasource UID is resolved to "grafana" assert.NotNil(t, query.Spec.Datasource.Uid) - assert.Equal(t, grafanads.DatasourceUID, *query.Spec.Datasource.Uid, "type: 'datasource' with no UID should resolve to uid: 'grafana'") + assert.Equal(t, dashboard.GrafanaDatasourceUID, *query.Spec.Datasource.Uid, "type: 'datasource' with no UID should resolve to uid: 'grafana'") // Verify query kind matches datasource type assert.Equal(t, "datasource", query.Spec.Query.Kind) diff --git a/pkg/tsdb/grafanads/grafana.go b/pkg/tsdb/grafanads/grafana.go index 06254b7f0b8..68dc792342c 100644 --- a/pkg/tsdb/grafanads/grafana.go +++ b/pkg/tsdb/grafanads/grafana.go @@ -12,6 +12,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/datasources" @@ -31,7 +32,7 @@ const DatasourceID = -1 // DatasourceUID is the fake datasource uid used in requests to identify it as a // Grafana DS command. -const DatasourceUID = "grafana" +const DatasourceUID = dashboard.GrafanaDatasourceUID // Make sure Service implements required interfaces. // This is important to do since otherwise we will only get a From b19e5462545c80705e26a1d0489bae2ccac1b702 Mon Sep 17 00:00:00 2001 From: Santiago Date: Fri, 5 Dec 2025 16:04:42 +0100 Subject: [PATCH 25/48] Remote Alertmanager: Remove X-Remote-Alertmanager header (#114917) Remote Alertmanager: Remove X-Remote-Alertmanager haeder --- pkg/services/ngalert/remote/alertmanager_test.go | 7 ------- .../ngalert/remote/client/mimir_auth_round_tripper.go | 4 +--- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/pkg/services/ngalert/remote/alertmanager_test.go b/pkg/services/ngalert/remote/alertmanager_test.go index 953ef3ef2d6..d6f66756454 100644 --- a/pkg/services/ngalert/remote/alertmanager_test.go +++ b/pkg/services/ngalert/remote/alertmanager_test.go @@ -153,7 +153,6 @@ func TestGetRemoteState(t *testing.T) { getOkHandler := func(state string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { require.Equal(t, tenantID, r.Header.Get(client.MimirTenantHeader)) - require.Equal(t, "true", r.Header.Get(client.RemoteAlertmanagerHeader)) res := map[string]any{ "status": "success", @@ -268,7 +267,6 @@ func TestIntegrationApplyConfig(t *testing.T) { errorHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, tenantID, r.Header.Get(client.MimirTenantHeader)) - require.Equal(t, "true", r.Header.Get(client.RemoteAlertmanagerHeader)) w.Header().Add("content-type", "application/json") w.WriteHeader(http.StatusInternalServerError) require.NoError(t, json.NewEncoder(w).Encode(map[string]string{"status": "error"})) @@ -278,7 +276,6 @@ func TestIntegrationApplyConfig(t *testing.T) { var configSyncs, stateSyncs int okHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, tenantID, r.Header.Get(client.MimirTenantHeader)) - require.Equal(t, "true", r.Header.Get(client.RemoteAlertmanagerHeader)) res := map[string]any{"status": "success"} if r.Method == http.MethodPost { @@ -432,7 +429,6 @@ func TestCompareAndSendConfiguration(t *testing.T) { var got string server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, tenantID, r.Header.Get(client.MimirTenantHeader)) - require.Equal(t, "true", r.Header.Get(client.RemoteAlertmanagerHeader)) w.Header().Add("content-type", "application/json") b, err := io.ReadAll(r.Body) @@ -639,7 +635,6 @@ func Test_TestReceiversDecryptsSecureSettings(t *testing.T) { var got apimodels.TestReceiversConfigBodyParams server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, tenantID, r.Header.Get(client.MimirTenantHeader)) - require.Equal(t, "true", r.Header.Get(client.RemoteAlertmanagerHeader)) w.Header().Add("Content-Type", "application/json") require.NoError(t, json.NewDecoder(r.Body).Decode(&got)) require.NoError(t, r.Body.Close()) @@ -746,7 +741,6 @@ func TestApplyConfigWithExtraConfigs(t *testing.T) { var configSent client.UserGrafanaConfig server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, tenantID, r.Header.Get(client.MimirTenantHeader)) - require.Equal(t, "true", r.Header.Get(client.RemoteAlertmanagerHeader)) if r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/config") { require.NoError(t, json.NewDecoder(r.Body).Decode(&configSent)) @@ -828,7 +822,6 @@ func TestCompareAndSendConfigurationWithExtraConfigs(t *testing.T) { var configSent client.UserGrafanaConfig server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, tenantID, r.Header.Get(client.MimirTenantHeader)) - require.Equal(t, "true", r.Header.Get(client.RemoteAlertmanagerHeader)) if r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/config") { require.NoError(t, json.NewDecoder(r.Body).Decode(&configSent)) diff --git a/pkg/services/ngalert/remote/client/mimir_auth_round_tripper.go b/pkg/services/ngalert/remote/client/mimir_auth_round_tripper.go index 3a9ff94f4a6..2a7a6314e1d 100644 --- a/pkg/services/ngalert/remote/client/mimir_auth_round_tripper.go +++ b/pkg/services/ngalert/remote/client/mimir_auth_round_tripper.go @@ -5,8 +5,7 @@ import ( ) const ( - MimirTenantHeader = "X-Scope-OrgID" - RemoteAlertmanagerHeader = "X-Remote-Alertmanager" + MimirTenantHeader = "X-Scope-OrgID" ) type MimirAuthRoundTripper struct { @@ -19,7 +18,6 @@ type MimirAuthRoundTripper struct { // It adds an `X-Scope-OrgID` header with the TenantID if only provided with a tenantID or sets HTTP Basic Authentication if both // a tenantID and a password are provided. func (r *MimirAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - req.Header.Set(RemoteAlertmanagerHeader, "true") if r.TenantID != "" && r.Password == "" { req.Header.Set(MimirTenantHeader, r.TenantID) } From bf042afa9878fcbe91a14da88fb6373f0946c0eb Mon Sep 17 00:00:00 2001 From: Bogdan Matei Date: Fri, 5 Dec 2025 17:12:12 +0200 Subject: [PATCH 26/48] Dashboard: Fix dropping panels in tabs and rows (#114893) --- .../dashboard-scene/scene/layout-rows/RowItemRenderer.tsx | 3 ++- .../dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx index 4c74ee35d99..80c16b2f2a1 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItemRenderer.tsx @@ -13,6 +13,7 @@ import { isRepeatCloneOrChildOf } from '../../utils/clone'; import { useDashboardState, useInterpolatedTitle } from '../../utils/utils'; import { DashboardScene } from '../DashboardScene'; import { useSoloPanelContext } from '../SoloPanelContext'; +import { isDashboardLayoutGrid } from '../types/DashboardLayoutGrid'; import { RowItem } from './RowItem'; @@ -83,7 +84,7 @@ export function RowItemRenderer({ model }: SceneComponentProps) { dragProvided.innerRef(ref); model.containerRef.current = ref; }} - data-dashboard-drop-target-key={model.state.key} + data-dashboard-drop-target-key={isDashboardLayoutGrid(layout) ? model.state.key : undefined} className={cx( styles.wrapper, !isCollapsed && styles.wrapperNotCollapsed, diff --git a/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx index 0fa22e9d305..e618fb21c7e 100644 --- a/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-tabs/TabItemRenderer.tsx @@ -11,6 +11,7 @@ import { useIsConditionallyHidden } from '../../conditional-rendering/hooks/useI import { isRepeatCloneOrChildOf } from '../../utils/clone'; import { useDashboardState } from '../../utils/utils'; import { useSoloPanelContext } from '../SoloPanelContext'; +import { isDashboardLayoutGrid } from '../types/DashboardLayoutGrid'; import { TabItem } from './TabItem'; @@ -91,7 +92,7 @@ export function TabItemRenderer({ model }: SceneComponentProps) { onSelect?.(evt); }} label={titleInterpolated} - data-dashboard-drop-target-key={model.state.key} + data-dashboard-drop-target-key={isDashboardLayoutGrid(layout) ? model.state.key : undefined} {...titleCollisionProps} />
    From 7cd10aa49ed22435b0aef9fc04d471818b7108dc Mon Sep 17 00:00:00 2001 From: Sarah Zinger Date: Fri, 5 Dec 2025 10:14:02 -0500 Subject: [PATCH 27/48] SQL Expressions: Fix alerts with sql expressions that have a cte (#114852) Fix for #114377 - fix alerts with sql expressions that have a cte --- .../components/rule-editor/dag.test.ts | 55 +++++++++++++++++++ .../unified/components/rule-editor/dag.ts | 44 +++++++++++++-- 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/public/app/features/alerting/unified/components/rule-editor/dag.test.ts b/public/app/features/alerting/unified/components/rule-editor/dag.test.ts index 189bcb6f25e..423f035ddc9 100644 --- a/public/app/features/alerting/unified/components/rule-editor/dag.test.ts +++ b/public/app/features/alerting/unified/components/rule-editor/dag.test.ts @@ -293,6 +293,61 @@ SELECT * FROM table1`) expect(parseRefsFromSqlExpression('SELECT * FROM\ntable1')).toEqual(['table1']); }); }); + + describe('CTE (Common Table Expression) handling', () => { + it('should exclude single CTE name from results', () => { + const query = 'WITH my_cte AS (SELECT * FROM table1) SELECT * FROM my_cte'; + expect(parseRefsFromSqlExpression(query)).toEqual(['table1']); + }); + + it('should exclude multiple CTE names from results', () => { + const query = ` + WITH cte1 AS (SELECT * FROM table1), + cte2 AS (SELECT * FROM table2) + SELECT * FROM cte1 JOIN cte2 ON cte1.id = cte2.id + `; + expect(parseRefsFromSqlExpression(query)).toEqual(['table1', 'table2']); + }); + + it('should handle CTEs with external table references in main query', () => { + const query = ` + WITH summary AS (SELECT id, count FROM table1) + SELECT * FROM summary JOIN table2 ON summary.id = table2.id + `; + expect(parseRefsFromSqlExpression(query)).toEqual(['table1', 'table2']); + }); + + it('should handle CTE names case-insensitively', () => { + const query = 'WITH MyCte AS (SELECT * FROM table1) SELECT * FROM mycte'; + expect(parseRefsFromSqlExpression(query)).toEqual(['table1']); + }); + + it('should handle RECURSIVE CTEs', () => { + const query = ` + WITH RECURSIVE cte AS ( + SELECT * FROM table1 + UNION ALL + SELECT * FROM cte WHERE depth < 10 + ) + SELECT * FROM cte + `; + expect(parseRefsFromSqlExpression(query)).toEqual(['table1']); + }); + + it('should handle queries without CTEs normally', () => { + const query = 'SELECT * FROM table1 JOIN table2 ON table1.id = table2.id'; + expect(parseRefsFromSqlExpression(query)).toEqual(['table1', 'table2']); + }); + + it('should handle CTE that references another CTE', () => { + const query = ` + WITH cte1 AS (SELECT * FROM table1), + cte2 AS (SELECT * FROM cte1) + SELECT * FROM cte2 + `; + expect(parseRefsFromSqlExpression(query)).toEqual(['table1']); + }); + }); }); describe('fingerprints', () => { diff --git a/public/app/features/alerting/unified/components/rule-editor/dag.ts b/public/app/features/alerting/unified/components/rule-editor/dag.ts index 9666186f039..c7078ca7517 100644 --- a/public/app/features/alerting/unified/components/rule-editor/dag.ts +++ b/public/app/features/alerting/unified/components/rule-editor/dag.ts @@ -132,10 +132,15 @@ export function parseRefsFromSqlExpression(input: string): string[] { .replace(/\s+/g, ' ') // Remove any potential multi line comments .replace(/\/\*[\s\S]*?\*\//g, ''); + + // Extract CTE names to exclude them from table references + const cteNames = parseCteNames(query); + const tableMatches = []; // Extract tables after FROM - case insensitive with /i flag - const fromRegex = /from\s+([^;]*?)(?:\s+(?:join|where|group|having|order|limit)|\s*$)/gi; + // Terminate on: SQL keywords, closing paren (for CTEs/subqueries), or end of string + const fromRegex = /from\s+([^;)]*?)(?:\s+(?:join|where|group|having|order|limit|on|select)|\)|$)/gi; for (const match of query.matchAll(fromRegex)) { const fromClause = match[1].trim(); @@ -153,13 +158,44 @@ export function parseRefsFromSqlExpression(input: string): string[] { tableMatches.push(cleanTableName(match[1])); } - return compact(uniq(tableMatches)); + // Filter out CTE names - they're local definitions, not external references + const externalRefs = tableMatches.filter((table) => !cteNames.has(table.toLowerCase())); + + return compact(uniq(externalRefs)); +} + +/** + * Parse CTE (Common Table Expression) names from a SQL query. + * CTEs are defined with: WITH cte_name AS (...), another_cte AS (...) + */ +function parseCteNames(query: string): Set { + const cteNames = new Set(); + + // Match the WITH clause - handles both regular and RECURSIVE CTEs + const withMatch = query.match(/^\s*with\s+(?:recursive\s+)?(.*?)(?:\s+select\s)/i); + + if (!withMatch) { + return cteNames; + } + + const withClause = withMatch[1]; + + // Match CTE names - they appear before "AS" keyword followed by opening paren + // This handles: cte_name AS (, "quoted_name" AS ( + const cteNameRegex = /([a-zA-Z0-9_]+|"[^"]+"|'[^']+')\s+as\s*\(/gi; + + for (const match of withClause.matchAll(cteNameRegex)) { + const cteName = match[1].replace(/['"]/g, '').toLowerCase(); + cteNames.add(cteName); + } + + return cteNames; } // Helper function to clean table names function cleanTableName(tableName: string): string { - // Remove quotes - let name = tableName.replace(/['"]/g, ''); + // Remove quotes and parentheses + let name = tableName.replace(/['"()]/g, ''); // Remove alias if present (both "AS alias" and "alias" forms) if (name.includes(' as ')) { From 0adb2461e9bdd21c56412a2b9d3695fad6e06902 Mon Sep 17 00:00:00 2001 From: "Marc M." <146180665+grafakus@users.noreply.github.com> Date: Fri, 5 Dec 2025 16:48:34 +0100 Subject: [PATCH 28/48] Dashboards: Improve custom variable editor and undo/redo (#114559) --- .../dashboards-edit-custom-variables.spec.ts | 6 +- .../src/selectors/pages.ts | 3 + .../components/VariableValuesPreview.tsx | 2 +- .../CustomVariableEditor/ModalEditor.tsx | 92 +++++++++++++++---- .../editors/CustomVariableEditor/PaneItem.tsx | 2 +- .../CustomVariableEditor/ValuesBuilder.tsx | 52 ----------- .../CustomVariableEditor/ValuesPreview.tsx | 13 --- public/locales/en-US/grafana.json | 4 +- 8 files changed, 85 insertions(+), 89 deletions(-) delete mode 100644 public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ValuesBuilder.tsx delete mode 100644 public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ValuesPreview.tsx diff --git a/e2e-playwright/dashboard-new-layouts/dashboards-edit-custom-variables.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboards-edit-custom-variables.spec.ts index 4715dfc7128..e11f2dd099a 100644 --- a/e2e-playwright/dashboard-new-layouts/dashboards-edit-custom-variables.spec.ts +++ b/e2e-playwright/dashboard-new-layouts/dashboards-edit-custom-variables.spec.ts @@ -84,9 +84,9 @@ test.describe( refetchItems(dashboardPage, selectors); }; - const closeModal = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups) => { + const applyAndcloseModal = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups) => { await dashboardPage - .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.closeButton) + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.applyButton) .click(); }; @@ -149,7 +149,7 @@ test.describe( await removeItem(dashboardPage, selectors, 2); await checkRows(3); await checkPreview(dashboardPage, selectors, ['first value', 'second label', 'fourth value']); - await closeModal(dashboardPage, selectors); + await applyAndcloseModal(dashboardPage, selectors); // assert variable is visible and has the correct values const variableLabel = dashboardPage.getByGrafanaSelector( diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index 47a5573b00d..1fa640a2563 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -567,6 +567,9 @@ export const versionedPages = { closeButton: { [MIN_GRAFANA_VERSION]: 'data-testid custom-variable-close-button', }, + applyButton: { + [MIN_GRAFANA_VERSION]: 'data-testid custom-variable-apply-button', + }, }, IntervalVariable: { intervalsValueInput: { diff --git a/public/app/features/dashboard-scene/settings/variables/components/VariableValuesPreview.tsx b/public/app/features/dashboard-scene/settings/variables/components/VariableValuesPreview.tsx index 73d57bd811a..ac59419cda7 100644 --- a/public/app/features/dashboard-scene/settings/variables/components/VariableValuesPreview.tsx +++ b/public/app/features/dashboard-scene/settings/variables/components/VariableValuesPreview.tsx @@ -37,7 +37,7 @@ export const VariableValuesPreview = ({ options }: VariableValuesPreviewProps) = {previewOptions.map((o, index) => ( -
    {o.label}
    +
    {o.label || String(o.value)}
    ))} diff --git a/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ModalEditor.tsx b/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ModalEditor.tsx index 3e8a8aa57b1..aed926a6809 100644 --- a/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ModalEditor.tsx +++ b/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ModalEditor.tsx @@ -1,47 +1,103 @@ -import { useCallback, useRef } from 'react'; +import { useRef, useState } from 'react'; +import { lastValueFrom } from 'rxjs'; import { selectors } from '@grafana/e2e-selectors'; import { t, Trans } from '@grafana/i18n'; -import { CustomVariable } from '@grafana/scenes'; +import { CustomVariable, VariableValueOption, VariableValueSingle } from '@grafana/scenes'; import { Button, Modal, Stack } from '@grafana/ui'; -import { VariableStaticOptionsFormRef } from '../../components/VariableStaticOptionsForm'; +import { dashboardEditActions } from '../../../../edit-pane/shared'; +import { VariableStaticOptionsForm, VariableStaticOptionsFormRef } from '../../components/VariableStaticOptionsForm'; import { VariableStaticOptionsFormAddButton } from '../../components/VariableStaticOptionsFormAddButton'; - -import { ValuesBuilder } from './ValuesBuilder'; -import { ValuesPreview } from './ValuesPreview'; +import { VariableValuesPreview } from '../../components/VariableValuesPreview'; interface ModalEditorProps { variable: CustomVariable; - isOpen: boolean; onClose: () => void; } -export function ModalEditor({ variable, isOpen, onClose }: ModalEditorProps) { - const formRef = useRef(null); - - const handleOnAdd = useCallback(() => formRef.current?.addItem(), []); +export function ModalEditor(props: ModalEditorProps) { + const { formRef, onCloseModal, options, onChangeOptions, onAddNewOption, onSaveOptions } = useModalEditor(props); return ( - - + + - }> + }> + ); } + +function useModalEditor({ variable, onClose }: ModalEditorProps) { + const { query } = variable.state; + const [options, setOptions] = useState(() => transformQueryToOptions(variable, query)); + const initialQueryRef = useRef(query); + const formRef = useRef(null); + + return { + formRef, + onCloseModal: onClose, + options, + onChangeOptions: setOptions, + onAddNewOption() { + formRef.current?.addItem(); + }, + onSaveOptions() { + dashboardEditActions.edit({ + source: variable, + description: t('dashboard.edit-pane.variable.custom-options.change-value', 'Change variable value'), + perform: () => { + variable.setState({ query: transformOptionsToQuery(options) }); + lastValueFrom(variable.validateAndUpdate!()); + }, + undo: () => { + variable.setState({ query: initialQueryRef.current }); + lastValueFrom(variable.validateAndUpdate!()); + }, + }); + + onClose(); + }, + }; +} + +const transformQueryToOptions = (variable: ModalEditorProps['variable'], query: string) => + variable.transformCsvStringToOptions(query, false).map(({ label, value }) => ({ + value, + label: value === label ? '' : label, + })); + +const formatOption = (option: VariableValueOption) => { + if (!option.label || option.label === option.value) { + return escapeEntities(option.value); + } + return `${escapeEntities(option.label)} : ${escapeEntities(String(option.value))}`; +}; + +const escapeEntities = (text: VariableValueSingle) => String(text).trim().replaceAll(',', '\\,'); + +const transformOptionsToQuery = (options: VariableValueOption[]) => options.map(formatOption).join(', '); diff --git a/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/PaneItem.tsx b/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/PaneItem.tsx index e453fc6b8b8..d1dab1e554f 100644 --- a/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/PaneItem.tsx +++ b/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/PaneItem.tsx @@ -31,7 +31,7 @@ export function PaneItem({ variable }: PaneItemProps) { Open variable editor - setIsOpen(false)} /> + {isOpen && setIsOpen(false)} />} ); } diff --git a/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ValuesBuilder.tsx b/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ValuesBuilder.tsx deleted file mode 100644 index e2eceea5fd3..00000000000 --- a/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ValuesBuilder.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { forwardRef, useCallback } from 'react'; -import { lastValueFrom } from 'rxjs'; - -import { CustomVariable, VariableValueOption, VariableValueSingle } from '@grafana/scenes'; - -import { VariableStaticOptionsForm, VariableStaticOptionsFormRef } from '../../components/VariableStaticOptionsForm'; - -interface ValuesBuilderProps { - variable: CustomVariable; -} - -export const ValuesBuilder = forwardRef(function ( - { variable }: ValuesBuilderProps, - ref -) { - const { query } = variable.useState(); - - const options = variable.transformCsvStringToOptions(query, false).map(({ label, value }) => ({ - value, - label: value === label ? '' : label, - })); - - const escapeEntities = useCallback((text: VariableValueSingle) => String(text).trim().replaceAll(',', '\\,'), []); - - const formatOption = useCallback( - (option: VariableValueOption) => { - if (!option.label || option.label === option.value) { - return escapeEntities(option.value); - } - - return `${escapeEntities(option.label)} : ${escapeEntities(String(option.value))}`; - }, - [escapeEntities] - ); - - const generateQuery = useCallback( - (options: VariableValueOption[]) => options.map(formatOption).join(', '), - [formatOption] - ); - - const handleOptionsChange = useCallback( - async (options: VariableValueOption[]) => { - variable.setState({ query: generateQuery(options) }); - await lastValueFrom(variable.validateAndUpdate!()); - }, - [variable, generateQuery] - ); - - return ; -}); - -ValuesBuilder.displayName = 'ValuesBuilder'; diff --git a/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ValuesPreview.tsx b/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ValuesPreview.tsx deleted file mode 100644 index 49a3e8dd55b..00000000000 --- a/public/app/features/dashboard-scene/settings/variables/editors/CustomVariableEditor/ValuesPreview.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { CustomVariable } from '@grafana/scenes'; - -import { VariableValuesPreview } from '../../components/VariableValuesPreview'; -import { hasVariableOptions } from '../../utils'; - -export function ValuesPreview({ variable }: { variable: CustomVariable }) { - // Workaround to toggle a component refresh when values change so that the preview is updated - variable.useState(); - - const isHasVariableOptions = hasVariableOptions(variable); - - return isHasVariableOptions ? : null; -} diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 6ee3b9e50e6..cc056e38526 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -4811,7 +4811,9 @@ }, "variable": { "custom-options": { - "close": "Close", + "apply": "Apply", + "change-value": "Change variable value", + "discard": "Discard", "modal-title": "Custom Variable", "values": "Values separated by comma" }, From 74c7b5a29220301d8809d2fc0caad2efe9f4e853 Mon Sep 17 00:00:00 2001 From: Alexander Akhmetov Date: Fri, 5 Dec 2025 18:02:11 +0100 Subject: [PATCH 29/48] Alerting: Fix creating a new alert rule vesion when only keep_firing_for changes (#114926) Alerting: Create alert rule vesion when keep_firing_for changes --- pkg/services/ngalert/store/models.go | 1 + pkg/services/ngalert/store/models_test.go | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/pkg/services/ngalert/store/models.go b/pkg/services/ngalert/store/models.go index 24167a225dd..25b81053e66 100644 --- a/pkg/services/ngalert/store/models.go +++ b/pkg/services/ngalert/store/models.go @@ -88,6 +88,7 @@ func (a alertRuleVersion) EqualSpec(b alertRuleVersion) bool { a.NoDataState == b.NoDataState && a.ExecErrState == b.ExecErrState && a.For == b.For && + a.KeepFiringFor == b.KeepFiringFor && a.Annotations == b.Annotations && a.Labels == b.Labels && a.IsPaused == b.IsPaused && diff --git a/pkg/services/ngalert/store/models_test.go b/pkg/services/ngalert/store/models_test.go index ddbc34b4036..98703609007 100644 --- a/pkg/services/ngalert/store/models_test.go +++ b/pkg/services/ngalert/store/models_test.go @@ -21,6 +21,7 @@ func TestAlertRuleVersion_EqualSpec(t *testing.T) { NoDataState: "state1", ExecErrState: "state2", For: time.Minute, + KeepFiringFor: 2 * time.Minute, Annotations: `{ "test": "annotation" }`, Labels: `{ "test": "label" }`, IsPaused: true, @@ -119,6 +120,12 @@ func TestAlertRuleVersion_EqualSpec(t *testing.T) { b: func() alertRuleVersion { v := baseVersion; v.For = 2 * time.Minute; return v }(), expect: false, }, + { + name: "different KeepFiringFor durations", + a: baseVersion, + b: func() alertRuleVersion { v := baseVersion; v.KeepFiringFor = 5 * time.Minute; return v }(), + expect: false, + }, { name: "exact match including bools and other types", a: func() alertRuleVersion { From 5b89d3b807d06836a4d7e281bb37cbd1d5dae715 Mon Sep 17 00:00:00 2001 From: Todd Treece <360020+toddtreece@users.noreply.github.com> Date: Fri, 5 Dec 2025 12:56:01 -0500 Subject: [PATCH 30/48] Plugins App: Add access control (#114869) --- pkg/registry/apps/plugins/accesscontrol.go | 127 ++++++++++++++++++ pkg/registry/apps/plugins/register.go | 21 ++- pkg/server/wire_gen.go | 4 +- pkg/services/accesscontrol/permreg/permreg.go | 2 + 4 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 pkg/registry/apps/plugins/accesscontrol.go diff --git a/pkg/registry/apps/plugins/accesscontrol.go b/pkg/registry/apps/plugins/accesscontrol.go new file mode 100644 index 00000000000..d41efa86f97 --- /dev/null +++ b/pkg/registry/apps/plugins/accesscontrol.go @@ -0,0 +1,127 @@ +package plugins + +import ( + "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/org" +) + +const ( + // Plugins + ActionPluginsPluginsCreate = "plugins.plugins:create" // CREATE. + ActionPluginsPluginsWrite = "plugins.plugins:write" // UPDATE. + ActionPluginsPluginsRead = "plugins.plugins:read" // GET + LIST. + ActionPluginsPluginsDelete = "plugins.plugins:delete" // DELETE. + + // PluginMetas + ActionPluginsPluginsMetaCreate = "plugins.pluginsmeta:create" // CREATE. + ActionPluginsPluginsMetaWrite = "plugins.pluginsmeta:write" // UPDATE. + ActionPluginsPluginsMetaRead = "plugins.pluginsmeta:read" // GET + LIST. + ActionPluginsPluginsMetaDelete = "plugins.pluginsmeta:delete" // DELETE. +) + +var ( + ScopeProviderPluginsPlugins = accesscontrol.NewScopeProvider("plugins.plugins") + ScopeProviderPluginsPluginsMeta = accesscontrol.NewScopeProvider("plugins.pluginsmeta") + + ScopeAllPluginsPlugins = ScopeProviderPluginsPlugins.GetResourceAllScope() + ScopeAllPluginsPluginsMeta = ScopeProviderPluginsPluginsMeta.GetResourceAllScope() +) + +func registerAccessControlRoles(service accesscontrol.Service) error { + // Plugins + pluginsReader := accesscontrol.RoleRegistration{ + Role: accesscontrol.RoleDTO{ + Name: "fixed:plugins.plugins:reader", + DisplayName: "Plugins Reader", + Description: "Read and list plugins.", + Group: "Plugins", + Permissions: []accesscontrol.Permission{ + { + Action: ActionPluginsPluginsRead, + Scope: ScopeAllPluginsPlugins, + }, + }, + }, + Grants: []string{string(org.RoleViewer), string(org.RoleEditor), string(org.RoleAdmin)}, + } + + pluginsWriter := accesscontrol.RoleRegistration{ + Role: accesscontrol.RoleDTO{ + Name: "fixed:plugins.plugins:writer", + DisplayName: "Plugins Writer", + Description: "Create, update and delete plugins.", + Group: "Plugins", + Permissions: []accesscontrol.Permission{ + { + Action: ActionPluginsPluginsCreate, + Scope: ScopeAllPluginsPlugins, + }, + { + Action: ActionPluginsPluginsRead, + Scope: ScopeAllPluginsPlugins, + }, + { + Action: ActionPluginsPluginsWrite, + Scope: ScopeAllPluginsPlugins, + }, + { + Action: ActionPluginsPluginsDelete, + Scope: ScopeAllPluginsPlugins, + }, + }, + }, + Grants: []string{string(org.RoleAdmin)}, + } + + // PluginMetas + pluginsMetaReader := accesscontrol.RoleRegistration{ + Role: accesscontrol.RoleDTO{ + Name: "fixed:plugins.pluginsmeta:reader", + DisplayName: "Plugin Metas Reader", + Description: "Read and list plugin metadata.", + Group: "Plugins", + Permissions: []accesscontrol.Permission{ + { + Action: ActionPluginsPluginsMetaRead, + Scope: ScopeAllPluginsPluginsMeta, + }, + }, + }, + Grants: []string{string(org.RoleViewer), string(org.RoleEditor), string(org.RoleAdmin)}, + } + + pluginsMetaWriter := accesscontrol.RoleRegistration{ + Role: accesscontrol.RoleDTO{ + Name: "fixed:plugins.pluginsmeta:writer", + DisplayName: "Plugin Metas Writer", + Description: "Create, update and delete plugin metadata.", + Group: "Plugins", + Permissions: []accesscontrol.Permission{ + { + Action: ActionPluginsPluginsMetaCreate, + Scope: ScopeAllPluginsPluginsMeta, + }, + { + Action: ActionPluginsPluginsMetaRead, + Scope: ScopeAllPluginsPluginsMeta, + }, + { + Action: ActionPluginsPluginsMetaWrite, + Scope: ScopeAllPluginsPluginsMeta, + }, + { + Action: ActionPluginsPluginsMetaDelete, + Scope: ScopeAllPluginsPluginsMeta, + }, + }, + }, + Grants: []string{string(org.RoleAdmin)}, + } + + return service.DeclareFixedRoles( + pluginsReader, + pluginsWriter, + pluginsMetaReader, + pluginsMetaWriter, + ) +} diff --git a/pkg/registry/apps/plugins/register.go b/pkg/registry/apps/plugins/register.go index 5d452cbe67c..6831d31ef9b 100644 --- a/pkg/registry/apps/plugins/register.go +++ b/pkg/registry/apps/plugins/register.go @@ -1,14 +1,16 @@ package plugins import ( + "fmt" "os" - "k8s.io/apiserver/pkg/authorization/authorizer" - + authlib "github.com/grafana/authlib/types" appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver" + "k8s.io/apiserver/pkg/authorization/authorizer" pluginsapp "github.com/grafana/grafana/apps/plugins/pkg/app" "github.com/grafana/grafana/apps/plugins/pkg/app/meta" + "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/apiserver/appinstaller" ) @@ -18,10 +20,14 @@ var ( ) type AppInstaller struct { - appsdkapiserver.AppInstaller + *pluginsapp.PluginAppInstaller } -func ProvideAppInstaller() (*AppInstaller, error) { +func ProvideAppInstaller(accessControlService accesscontrol.Service, accessClient authlib.AccessClient) (*AppInstaller, error) { + if err := registerAccessControlRoles(accessControlService); err != nil { + return nil, fmt.Errorf("registering access control roles: %w", err) + } + grafanaComAPIURL := os.Getenv("GRAFANA_COM_API_URL") if grafanaComAPIURL == "" { grafanaComAPIURL = "https://grafana.com/api/plugins" @@ -36,12 +42,13 @@ func ProvideAppInstaller() (*AppInstaller, error) { return nil, err } + i.WithAccessChecker(accessClient) + return &AppInstaller{ - AppInstaller: i, + PluginAppInstaller: i, }, nil } -// GetAuthorizer returns the authorizer for the plugins app. -func (p *AppInstaller) GetAuthorizer() authorizer.Authorizer { +func (a *AppInstaller) GetAuthorizer() authorizer.Authorizer { return pluginsapp.GetAuthorizer() } diff --git a/pkg/server/wire_gen.go b/pkg/server/wire_gen.go index e920bdbec61..d1a2ecee64f 100644 --- a/pkg/server/wire_gen.go +++ b/pkg/server/wire_gen.go @@ -783,7 +783,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api if err != nil { return nil, err } - appInstaller, err := plugins.ProvideAppInstaller() + appInstaller, err := plugins.ProvideAppInstaller(acimplService, accessClient) if err != nil { return nil, err } @@ -1436,7 +1436,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac if err != nil { return nil, err } - appInstaller, err := plugins.ProvideAppInstaller() + appInstaller, err := plugins.ProvideAppInstaller(acimplService, accessClient) if err != nil { return nil, err } diff --git a/pkg/services/accesscontrol/permreg/permreg.go b/pkg/services/accesscontrol/permreg/permreg.go index 5d025a1258b..c9f010c2909 100644 --- a/pkg/services/accesscontrol/permreg/permreg.go +++ b/pkg/services/accesscontrol/permreg/permreg.go @@ -84,6 +84,8 @@ func newPermissionRegistry() *permissionRegistry { "annotations": "annotations:type:", "orgs": "orgs:id:", "plugins": "plugins:id:", + "plugins.plugins": "plugins.plugins:uid:", + "plugins.pluginsmeta": "plugins.pluginsmeta:uid:", "provisioners": "provisioners:", "reports": "reports:id:", "permissions": "permissions:type:", From d1cbef9157ef70282e56b83b1a2f91ac418c6caa Mon Sep 17 00:00:00 2001 From: Charandas <542168+charandas@users.noreply.github.com> Date: Fri, 5 Dec 2025 11:53:31 -0800 Subject: [PATCH 31/48] K8s: use runtime config for API Builders (#114601) * Reapply "K8s: read resource configs from API Enablement for API Builders" (#114475) This reverts commit 4130bd9cd300ec7ce0fb492b3a7229968c9167b4. * revert part that broke things * FF service changes are gonna come later --- pkg/services/apiserver/builder/helper.go | 85 +++++++++++++------ pkg/services/apiserver/builder/openapi.go | 19 ++++- .../apiserver/builder/request_handler.go | 10 ++- pkg/services/apiserver/service.go | 8 +- 4 files changed, 90 insertions(+), 32 deletions(-) diff --git a/pkg/services/apiserver/builder/helper.go b/pkg/services/apiserver/builder/helper.go index 603c49f3cb2..a76a01dffba 100644 --- a/pkg/services/apiserver/builder/helper.go +++ b/pkg/services/apiserver/builder/helper.go @@ -22,6 +22,7 @@ import ( k8srequest "k8s.io/apiserver/pkg/endpoints/request" "k8s.io/apiserver/pkg/registry/generic" genericapiserver "k8s.io/apiserver/pkg/server" + serverstorage "k8s.io/apiserver/pkg/server/storage" "k8s.io/apiserver/pkg/util/openapi" k8sscheme "k8s.io/client-go/kubernetes/scheme" k8stracing "k8s.io/component-base/tracing" @@ -78,7 +79,9 @@ func GetDefaultBuildHandlerChainFunc(builders []APIGroupBuilder, reg prometheus. delegateHandler, c.LoopbackClientConfig, builders, - reg) + reg, + c.MergedResourceConfig, + ) if err != nil { panic(fmt.Sprintf("could not build the request handler for specified API builders: %s", err.Error())) } @@ -105,6 +108,8 @@ func GetDefaultBuildHandlerChainFunc(builders []APIGroupBuilder, reg prometheus. } } +// SetupConfig sets up the server config for the API server +// specify isAggregator=true, if the chain is being constructed for kube-aggregator func SetupConfig( scheme *runtime.Scheme, serverConfig *genericapiserver.RecommendedConfig, @@ -114,6 +119,7 @@ func SetupConfig( gvs []schema.GroupVersion, additionalOpenAPIDefGetters []common.GetOpenAPIDefinitions, reg prometheus.Registerer, + apiResourceConfig *serverstorage.ResourceConfig, ) error { serverConfig.AdmissionControl = NewAdmissionFromBuilders(builders) defsGetter := GetOpenAPIDefinitions(builders, additionalOpenAPIDefGetters...) @@ -126,7 +132,7 @@ func SetupConfig( openapinamer.NewDefinitionNamer(scheme, k8sscheme.Scheme)) // Add the custom routes to service discovery - serverConfig.OpenAPIV3Config.PostProcessSpec = getOpenAPIPostProcessor(buildVersion, builders, gvs) + serverConfig.OpenAPIV3Config.PostProcessSpec = getOpenAPIPostProcessor(buildVersion, builders, gvs, apiResourceConfig) serverConfig.OpenAPIV3Config.GetOperationIDAndTagsFromRoute = func(r common.Route) (string, []string, error) { meta := r.Metadata() kind := "" @@ -287,6 +293,7 @@ func InstallAPIs( features featuremgmt.FeatureToggles, dualWriterMetrics *grafanarest.DualWriterMetrics, builderMetrics *BuilderMetrics, + apiResourceConfig *serverstorage.ResourceConfig, ) error { // dual writing is only enabled when the storage type is not legacy. // this is needed to support setting a default RESTOptionsGetter for new APIs that don't @@ -401,34 +408,9 @@ func InstallAPIs( for group, buildersForGroup := range buildersGroupMap { g := genericapiserver.NewDefaultAPIGroupInfo(group, scheme, metav1.ParameterCodec, codecs) for _, b := range buildersForGroup { - if err := b.UpdateAPIGroupInfo(&g, APIGroupOptions{ - Scheme: scheme, - OptsGetter: optsGetter, - DualWriteBuilder: dualWrite, - MetricsRegister: reg, - StorageOptsRegister: optsregister, - StorageOpts: storageOpts, - }); err != nil { + if err := installAPIGroupsForBuilder(&g, group, b, apiResourceConfig, scheme, optsGetter, dualWrite, reg, optsregister, storageOpts, features); err != nil { return err } - if len(g.PrioritizedVersions) < 1 { - continue - } - - // if grafanaAPIServerWithExperimentalAPIs is not enabled, remove v0alpha1 resources unless explicitly allowed - //nolint:staticcheck // not yet migrated to OpenFeature - if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) { - if resources, ok := g.VersionedResourcesStorageMap["v0alpha1"]; ok { - for name := range resources { - if !allowRegisteringResourceByInfo(b.AllowedV0Alpha1Resources(), name) { - delete(resources, name) - } - } - if len(resources) == 0 { - delete(g.VersionedResourcesStorageMap, "v0alpha1") - } - } - } } // skip installing the group if there are no resources left after filtering @@ -445,6 +427,53 @@ func InstallAPIs( return nil } +func installAPIGroupsForBuilder(g *genericapiserver.APIGroupInfo, group string, b APIGroupBuilder, apiResourceConfig *serverstorage.ResourceConfig, scheme *runtime.Scheme, + optsGetter generic.RESTOptionsGetter, dualWrite grafanarest.DualWriteBuilder, reg prometheus.Registerer, optsregister apistore.StorageOptionsRegister, + storageOpts *options.StorageOptions, features featuremgmt.FeatureToggles) error { + if err := b.UpdateAPIGroupInfo(g, APIGroupOptions{ + Scheme: scheme, + OptsGetter: optsGetter, + DualWriteBuilder: dualWrite, + MetricsRegister: reg, + StorageOptsRegister: optsregister, + StorageOpts: storageOpts, + }); err != nil { + return err + } + if len(g.PrioritizedVersions) < 1 { + return nil + } + + // filter out api groups that are disabled in APIEnablementOptions + for version := range g.VersionedResourcesStorageMap { + gvr := schema.GroupVersionResource{ + Group: group, + Version: version, + } + if apiResourceConfig != nil && !apiResourceConfig.ResourceEnabled(gvr) { + klog.InfoS("Skipping storage for disabled resource", "gvr", gvr.String()) + delete(g.VersionedResourcesStorageMap, version) + } + } + + // if grafanaAPIServerWithExperimentalAPIs is not enabled, remove v0alpha1 resources unless explicitly allowed + //nolint:staticcheck // not yet migrated to OpenFeature + if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) { + if resources, ok := g.VersionedResourcesStorageMap["v0alpha1"]; ok { + for name := range resources { + if !allowRegisteringResourceByInfo(b.AllowedV0Alpha1Resources(), name) { + delete(resources, name) + } + } + if len(resources) == 0 { + delete(g.VersionedResourcesStorageMap, "v0alpha1") + } + } + } + + return nil +} + // AddPostStartHooks adds post start hooks to a generic API server config func AddPostStartHooks( config *genericapiserver.RecommendedConfig, diff --git a/pkg/services/apiserver/builder/openapi.go b/pkg/services/apiserver/builder/openapi.go index 9cb7cc17f7c..6d3b33f4baf 100644 --- a/pkg/services/apiserver/builder/openapi.go +++ b/pkg/services/apiserver/builder/openapi.go @@ -9,6 +9,8 @@ import ( apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/runtime/schema" + serverstorage "k8s.io/apiserver/pkg/server/storage" + "k8s.io/klog/v2" openapi "k8s.io/kube-openapi/pkg/common" "k8s.io/kube-openapi/pkg/spec3" spec "k8s.io/kube-openapi/pkg/validation/spec" @@ -76,6 +78,7 @@ func addBuilderRoutes( targetGroupVersion schema.GroupVersion, openAPISpec *spec3.OpenAPI, apiGroupBuilders []APIGroupBuilder, + apiResourceConfig *serverstorage.ResourceConfig, ) (*spec3.OpenAPI, error) { for _, apiGroupBuilder := range apiGroupBuilders { // Optionally include raw http handlers for all builders @@ -107,12 +110,24 @@ func addBuilderRoutes( } } } + + // filter out api groups that are disabled in APIEnablementOptions + for path := range openAPISpec.Paths.Paths { + if strings.HasPrefix(path, "/apis/"+targetGroupVersion.String()+"/") { + gv := targetGroupVersion.WithResource("") + if apiResourceConfig != nil && !apiResourceConfig.ResourceEnabled(gv) { + klog.InfoS("removing openapi routes for disabled resource", "gv", gv.String()) + delete(openAPISpec.Paths.Paths, path) + } + } + } + return openAPISpec, nil } // Modify the OpenAPI spec to include the additional routes. // nolint:gocyclo -func getOpenAPIPostProcessor(version string, builders []APIGroupBuilder, gvs []schema.GroupVersion) func(*spec3.OpenAPI) (*spec3.OpenAPI, error) { +func getOpenAPIPostProcessor(version string, builders []APIGroupBuilder, gvs []schema.GroupVersion, apiResourceConfig *serverstorage.ResourceConfig) func(*spec3.OpenAPI) (*spec3.OpenAPI, error) { return func(s *spec3.OpenAPI) (*spec3.OpenAPI, error) { if s.Paths == nil { return s, nil @@ -227,7 +242,7 @@ func getOpenAPIPostProcessor(version string, builders []APIGroupBuilder, gvs []s } } } - return addBuilderRoutes(gv, ©, builders) + return addBuilderRoutes(gv, ©, builders, apiResourceConfig) } } return s, nil diff --git a/pkg/services/apiserver/builder/request_handler.go b/pkg/services/apiserver/builder/request_handler.go index 80fbf6d0eaa..50761f1d42c 100644 --- a/pkg/services/apiserver/builder/request_handler.go +++ b/pkg/services/apiserver/builder/request_handler.go @@ -6,7 +6,9 @@ import ( "github.com/gorilla/mux" "github.com/prometheus/client_golang/prometheus" + serverstorage "k8s.io/apiserver/pkg/server/storage" restclient "k8s.io/client-go/rest" + klog "k8s.io/klog/v2" "k8s.io/kube-openapi/pkg/spec3" ) @@ -14,7 +16,7 @@ type requestHandler struct { router *mux.Router } -func GetCustomRoutesHandler(delegateHandler http.Handler, restConfig *restclient.Config, builders []APIGroupBuilder, metricsRegistry prometheus.Registerer) (http.Handler, error) { +func GetCustomRoutesHandler(delegateHandler http.Handler, restConfig *restclient.Config, builders []APIGroupBuilder, metricsRegistry prometheus.Registerer, apiResourceConfig *serverstorage.ResourceConfig) (http.Handler, error) { useful := false // only true if any routes exist anywhere router := mux.NewRouter() @@ -27,6 +29,12 @@ func GetCustomRoutesHandler(delegateHandler http.Handler, restConfig *restclient } for _, gv := range GetGroupVersions(builder) { + // filter out api groups that are disabled in APIEnablementOptions + gvr := gv.WithResource("") + if apiResourceConfig != nil && !apiResourceConfig.ResourceEnabled(gvr) { + klog.InfoS("Skipping custom route handler for disabled group version", "gv", gv.String()) + continue + } routes := provider.GetAPIRoutes(gv) if routes == nil { continue diff --git a/pkg/services/apiserver/service.go b/pkg/services/apiserver/service.go index 28da416536c..605016c112d 100644 --- a/pkg/services/apiserver/service.go +++ b/pkg/services/apiserver/service.go @@ -316,7 +316,11 @@ func (s *service) start(ctx context.Context) error { s.cfg.BuildBranch, ) - if err := o.APIEnablementOptions.ApplyTo(&serverConfig.Config, appinstaller.NewAPIResourceConfig(s.appInstallers), s.scheme); err != nil { + apiResourceConfig := appinstaller.NewAPIResourceConfig(s.appInstallers) + // add the builder group versions to the api resource config + apiResourceConfig.EnableVersions(groupVersions...) + + if err := o.APIEnablementOptions.ApplyTo(&serverConfig.Config, apiResourceConfig, s.scheme); err != nil { return err } @@ -359,6 +363,7 @@ func (s *service) start(ctx context.Context) error { groupVersions, defGetters, s.metrics, + apiResourceConfig, ) if err != nil { return err @@ -400,6 +405,7 @@ func (s *service) start(ctx context.Context) error { s.features, s.dualWriterMetrics, s.builderMetrics, + apiResourceConfig, ) if err != nil { return err From 0f9d0317dc2a2e2b362e48e25acbd992633029bd Mon Sep 17 00:00:00 2001 From: "grafana-pr-automation[bot]" <140550294+grafana-pr-automation[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 00:40:23 +0000 Subject: [PATCH 32/48] I18n: Download translations from Crowdin (#114938) New Crowdin translations by GitHub Action Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- public/locales/cs-CZ/grafana.json | 7 +++++-- public/locales/de-DE/grafana.json | 7 +++++-- public/locales/es-ES/grafana.json | 7 +++++-- public/locales/fr-FR/grafana.json | 7 +++++-- public/locales/hu-HU/grafana.json | 7 +++++-- public/locales/id-ID/grafana.json | 7 +++++-- public/locales/it-IT/grafana.json | 7 +++++-- public/locales/ja-JP/grafana.json | 7 +++++-- public/locales/ko-KR/grafana.json | 7 +++++-- public/locales/nl-NL/grafana.json | 7 +++++-- public/locales/pl-PL/grafana.json | 7 +++++-- public/locales/pt-BR/grafana.json | 7 +++++-- public/locales/pt-PT/grafana.json | 7 +++++-- public/locales/ru-RU/grafana.json | 7 +++++-- public/locales/sv-SE/grafana.json | 7 +++++-- public/locales/tr-TR/grafana.json | 7 +++++-- public/locales/zh-Hans/grafana.json | 7 +++++-- public/locales/zh-Hant/grafana.json | 7 +++++-- 18 files changed, 90 insertions(+), 36 deletions(-) diff --git a/public/locales/cs-CZ/grafana.json b/public/locales/cs-CZ/grafana.json index 99f381a244e..e90a588053c 100644 --- a/public/locales/cs-CZ/grafana.json +++ b/public/locales/cs-CZ/grafana.json @@ -4851,7 +4851,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Hodnoty oddělené čárkou" }, @@ -12502,7 +12504,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json index f84c77dd97f..e0a149baab7 100644 --- a/public/locales/de-DE/grafana.json +++ b/public/locales/de-DE/grafana.json @@ -4811,7 +4811,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Werte werden durch Komma getrennt" }, @@ -12396,7 +12398,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json index 70b7097bcc7..ff65f840041 100644 --- a/public/locales/es-ES/grafana.json +++ b/public/locales/es-ES/grafana.json @@ -4811,7 +4811,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Valores separados por coma" }, @@ -12396,7 +12398,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json index 098050412e8..01bbb43fff9 100644 --- a/public/locales/fr-FR/grafana.json +++ b/public/locales/fr-FR/grafana.json @@ -4811,7 +4811,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Valeurs séparées par une virgule" }, @@ -12396,7 +12398,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/hu-HU/grafana.json b/public/locales/hu-HU/grafana.json index 28130cb2ec2..146b2a78d04 100644 --- a/public/locales/hu-HU/grafana.json +++ b/public/locales/hu-HU/grafana.json @@ -4811,7 +4811,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Értékek vesszővel elválasztva" }, @@ -12396,7 +12398,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/id-ID/grafana.json b/public/locales/id-ID/grafana.json index 72598112d52..7b8e043e947 100644 --- a/public/locales/id-ID/grafana.json +++ b/public/locales/id-ID/grafana.json @@ -4791,7 +4791,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Nilai dipisahkan dengan koma" }, @@ -12343,7 +12345,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/it-IT/grafana.json b/public/locales/it-IT/grafana.json index 5b8c69dff0b..68364a78d6f 100644 --- a/public/locales/it-IT/grafana.json +++ b/public/locales/it-IT/grafana.json @@ -4811,7 +4811,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Valori separati da virgola" }, @@ -12396,7 +12398,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/ja-JP/grafana.json b/public/locales/ja-JP/grafana.json index 14c46a83f7f..c023581e3cf 100644 --- a/public/locales/ja-JP/grafana.json +++ b/public/locales/ja-JP/grafana.json @@ -4791,7 +4791,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "カンマで区切った値" }, @@ -12343,7 +12345,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/ko-KR/grafana.json b/public/locales/ko-KR/grafana.json index df4cad85150..e9cb03b8350 100644 --- a/public/locales/ko-KR/grafana.json +++ b/public/locales/ko-KR/grafana.json @@ -4791,7 +4791,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "쉼표로 구분된 값" }, @@ -12343,7 +12345,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/nl-NL/grafana.json b/public/locales/nl-NL/grafana.json index 126ad3f53f9..7872baf058a 100644 --- a/public/locales/nl-NL/grafana.json +++ b/public/locales/nl-NL/grafana.json @@ -4811,7 +4811,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Waarden gescheiden door komma" }, @@ -12396,7 +12398,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/pl-PL/grafana.json b/public/locales/pl-PL/grafana.json index 0f441a67b12..acd3b23e77f 100644 --- a/public/locales/pl-PL/grafana.json +++ b/public/locales/pl-PL/grafana.json @@ -4851,7 +4851,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Wartości rozdzielone przecinkami" }, @@ -12502,7 +12504,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json index a96c531b03c..e091960845a 100644 --- a/public/locales/pt-BR/grafana.json +++ b/public/locales/pt-BR/grafana.json @@ -4811,7 +4811,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Valores separados por vírgula" }, @@ -12396,7 +12398,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/pt-PT/grafana.json b/public/locales/pt-PT/grafana.json index dc7e3c11c34..3a69ffa3194 100644 --- a/public/locales/pt-PT/grafana.json +++ b/public/locales/pt-PT/grafana.json @@ -4811,7 +4811,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Valores separados por vírgulas" }, @@ -12396,7 +12398,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/ru-RU/grafana.json b/public/locales/ru-RU/grafana.json index cb1af71f6ed..a85c8b00dd5 100644 --- a/public/locales/ru-RU/grafana.json +++ b/public/locales/ru-RU/grafana.json @@ -4851,7 +4851,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Значения, разделенные запятыми" }, @@ -12502,7 +12504,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/sv-SE/grafana.json b/public/locales/sv-SE/grafana.json index 883cac40ddf..733bfdd230b 100644 --- a/public/locales/sv-SE/grafana.json +++ b/public/locales/sv-SE/grafana.json @@ -4811,7 +4811,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Värden åtskilda med kommatecken" }, @@ -12396,7 +12398,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/tr-TR/grafana.json b/public/locales/tr-TR/grafana.json index 2037475ec8a..4b26d75fcb5 100644 --- a/public/locales/tr-TR/grafana.json +++ b/public/locales/tr-TR/grafana.json @@ -4811,7 +4811,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "Virgülle ayrılmış değerler" }, @@ -12396,7 +12398,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json index 8d34dcd8286..45cbea59c45 100644 --- a/public/locales/zh-Hans/grafana.json +++ b/public/locales/zh-Hans/grafana.json @@ -4791,7 +4791,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "以逗号分隔的值" }, @@ -12343,7 +12345,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", diff --git a/public/locales/zh-Hant/grafana.json b/public/locales/zh-Hant/grafana.json index 97699c7bdd8..5548f9f3d56 100644 --- a/public/locales/zh-Hant/grafana.json +++ b/public/locales/zh-Hant/grafana.json @@ -4791,7 +4791,9 @@ }, "variable": { "custom-options": { - "close": "", + "apply": "", + "change-value": "", + "discard": "", "modal-title": "", "values": "以逗號分隔的值" }, @@ -12343,7 +12345,8 @@ "bar-glow": "", "center-glow": "", "rounded-bars": "", - "spotlight": "" + "spotlight": "", + "spotlight-tooltip": "" }, "gradient": "", "gradient-auto": "", From e9ba45ca4fd7f28d34cd2590762b75c6a7afd7c6 Mon Sep 17 00:00:00 2001 From: Steve Simpson Date: Sat, 6 Dec 2025 08:34:18 +0100 Subject: [PATCH 33/48] Update grafana-app-sdk to v0.48.5 (#114810) Co-authored-by: Owen Smallwood Co-authored-by: Ryan McKinley --- apps/advisor/go.mod | 2 +- apps/advisor/go.sum | 4 +- apps/alerting/alertenrichment/go.mod | 2 +- apps/alerting/alertenrichment/go.sum | 4 +- apps/alerting/historian/go.mod | 2 +- apps/alerting/historian/go.sum | 4 +- apps/alerting/notifications/go.mod | 2 +- apps/alerting/notifications/go.sum | 4 +- .../v0alpha1/receiver_object_gen.go | 6 + .../v0alpha1/receiver_schema_gen.go | 2 +- .../v0alpha1/routingtree_object_gen.go | 6 + .../v0alpha1/routingtree_schema_gen.go | 2 +- .../v0alpha1/templategroup_object_gen.go | 6 + .../v0alpha1/templategroup_schema_gen.go | 2 +- .../v0alpha1/timeinterval_object_gen.go | 6 + .../v0alpha1/timeinterval_schema_gen.go | 2 +- apps/alerting/rules/go.mod | 2 +- apps/alerting/rules/go.sum | 4 +- apps/annotation/go.mod | 2 +- apps/annotation/go.sum | 4 +- apps/collections/go.mod | 2 +- apps/collections/go.sum | 4 +- apps/correlations/go.mod | 2 +- apps/correlations/go.sum | 4 +- apps/dashboard/go.mod | 2 +- apps/dashboard/go.sum | 4 +- .../v0alpha1/dashboard_object_gen.go | 7 ++ .../v0alpha1/dashboard_schema_gen.go | 2 +- .../dashboard/v0alpha1/snapshot_object_gen.go | 6 + .../dashboard/v0alpha1/snapshot_schema_gen.go | 2 +- .../dashboard/v1beta1/dashboard_object_gen.go | 7 ++ .../dashboard/v1beta1/dashboard_schema_gen.go | 2 +- .../v2alpha1/dashboard_object_gen.go | 7 ++ .../v2alpha1/dashboard_schema_gen.go | 2 +- .../dashboard/v2beta1/dashboard_object_gen.go | 7 ++ .../dashboard/v2beta1/dashboard_schema_gen.go | 2 +- apps/example/go.mod | 2 +- apps/example/go.sum | 4 +- apps/folder/go.mod | 27 +++- apps/folder/go.sum | 69 ++++++++++- .../apis/folder/v1beta1/folder_object_gen.go | 6 + .../apis/folder/v1beta1/folder_schema_gen.go | 2 +- .../pkg/apis/manifestdata/folder_manifest.go | 116 ++++++++++++++++++ apps/iam/go.mod | 2 +- apps/iam/go.sum | 4 +- apps/investigations/go.mod | 2 +- apps/investigations/go.sum | 4 +- apps/logsdrilldown/go.mod | 2 +- apps/logsdrilldown/go.sum | 4 +- apps/playlist/go.mod | 2 +- apps/playlist/go.sum | 4 +- apps/plugins/go.mod | 17 +-- apps/plugins/go.sum | 24 ++-- apps/preferences/go.mod | 2 +- apps/preferences/go.sum | 4 +- apps/provisioning/go.mod | 2 +- apps/provisioning/go.sum | 4 +- apps/sdk.mk | 2 +- apps/secret/go.mod | 2 +- apps/secret/go.sum | 4 +- apps/shorturl/go.mod | 2 +- apps/shorturl/go.sum | 4 +- go.mod | 2 +- go.sum | 4 +- go.work.sum | 3 +- 65 files changed, 365 insertions(+), 91 deletions(-) create mode 100644 apps/folder/pkg/apis/manifestdata/folder_manifest.go diff --git a/apps/advisor/go.mod b/apps/advisor/go.mod index 2318df205ce..941d8b9bc0f 100644 --- a/apps/advisor/go.mod +++ b/apps/advisor/go.mod @@ -8,7 +8,7 @@ require ( github.com/google/go-github/v70 v70.0.0 github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 github.com/grafana/grafana v0.0.0-00010101000000-000000000000 - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 github.com/grafana/grafana-plugin-sdk-go v0.284.0 github.com/grafana/grafana/pkg/apimachinery v0.0.0 diff --git a/apps/advisor/go.sum b/apps/advisor/go.sum index 76208d30349..4695785bbd1 100644 --- a/apps/advisor/go.sum +++ b/apps/advisor/go.sum @@ -618,8 +618,8 @@ github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6k github.com/grafana/dataplane/sdata v0.0.9/go.mod h1:Jvs5ddpGmn6vcxT7tCTWAZ1mgi4sbcdFt9utQx5uMAU= 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.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana-aws-sdk v1.3.0 h1:/bfJzP93rCel1GbWoRSq0oUo424MZXt8jAp2BK9w8tM= diff --git a/apps/alerting/alertenrichment/go.mod b/apps/alerting/alertenrichment/go.mod index 95466bad6bb..bb020e52bff 100644 --- a/apps/alerting/alertenrichment/go.mod +++ b/apps/alerting/alertenrichment/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/alerting/alertenrichment go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28 k8s.io/apimachinery v0.34.2 k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 diff --git a/apps/alerting/alertenrichment/go.sum b/apps/alerting/alertenrichment/go.sum index 5dff965c88b..ef36568611c 100644 --- a/apps/alerting/alertenrichment/go.sum +++ b/apps/alerting/alertenrichment/go.sum @@ -23,8 +23,8 @@ github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7O github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28 h1:PgMfX4OPENz/iXmtDDIW9+poZY4UD0hhmXm7flVclDo= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250901080157-a0280d701b28/go.mod h1:av5N0Naq+8VV9MLF7zAkihy/mVq5UbS2EvRSJukDHlY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= diff --git a/apps/alerting/historian/go.mod b/apps/alerting/historian/go.mod index 5963ac8139c..21ad42c90af 100644 --- a/apps/alerting/historian/go.mod +++ b/apps/alerting/historian/go.mod @@ -6,7 +6,7 @@ require ( github.com/go-kit/log v0.2.1 github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 github.com/prometheus/client_golang v1.23.2 github.com/spf13/pflag v1.0.10 diff --git a/apps/alerting/historian/go.sum b/apps/alerting/historian/go.sum index 9e44308979b..b4d2d1dc2e2 100644 --- a/apps/alerting/historian/go.sum +++ b/apps/alerting/historian/go.sum @@ -216,14 +216,14 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= 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-20251204145817-de8c2bbf9eba h1:psKWNETD5nGxmFAlqnWsXoRyUwSa2GHNEMSEDKGKfQ4= github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU= 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.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/loki/pkg/push v0.0.0-20250823105456-332df2b20000 h1:/5LKSYgLmAhwA4m6iGUD4w1YkydEWWjazn9qxCFT8W0= diff --git a/apps/alerting/notifications/go.mod b/apps/alerting/notifications/go.mod index 80dd39f9ca7..f72c4c9775b 100644 --- a/apps/alerting/notifications/go.mod +++ b/apps/alerting/notifications/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/alerting/notifications go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 k8s.io/apimachinery v0.34.2 k8s.io/apiserver v0.34.2 diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index 6ab34248ef8..57b099ab2f1 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -71,8 +71,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/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= diff --git a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/receiver_object_gen.go b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/receiver_object_gen.go index 00bb1a34745..de0a0d5320f 100644 --- a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/receiver_object_gen.go +++ b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/receiver_object_gen.go @@ -23,6 +23,12 @@ type Receiver struct { Spec ReceiverSpec `json:"spec" yaml:"spec"` } +func NewReceiver() *Receiver { + return &Receiver{ + Spec: *NewReceiverSpec(), + } +} + func (o *Receiver) GetSpec() any { return o.Spec } diff --git a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/receiver_schema_gen.go b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/receiver_schema_gen.go index ea4e3b8e363..27047b00601 100644 --- a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/receiver_schema_gen.go +++ b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/receiver_schema_gen.go @@ -12,7 +12,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( - schemaReceiver = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", &Receiver{}, &ReceiverList{}, resource.WithKind("Receiver"), + schemaReceiver = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", NewReceiver(), &ReceiverList{}, resource.WithKind("Receiver"), resource.WithPlural("receivers"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{{ FieldSelector: "spec.title", FieldValueFunc: func(o resource.Object) (string, error) { diff --git a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/routingtree_object_gen.go b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/routingtree_object_gen.go index e59f0dada5c..354e009d77f 100644 --- a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/routingtree_object_gen.go +++ b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/routingtree_object_gen.go @@ -23,6 +23,12 @@ type RoutingTree struct { Spec RoutingTreeSpec `json:"spec" yaml:"spec"` } +func NewRoutingTree() *RoutingTree { + return &RoutingTree{ + Spec: *NewRoutingTreeSpec(), + } +} + func (o *RoutingTree) GetSpec() any { return o.Spec } diff --git a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/routingtree_schema_gen.go b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/routingtree_schema_gen.go index 6838c9cdebd..2a1812a2846 100644 --- a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/routingtree_schema_gen.go +++ b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/routingtree_schema_gen.go @@ -10,7 +10,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( - schemaRoutingTree = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", &RoutingTree{}, &RoutingTreeList{}, resource.WithKind("RoutingTree"), + schemaRoutingTree = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", NewRoutingTree(), &RoutingTreeList{}, resource.WithKind("RoutingTree"), resource.WithPlural("routingtrees"), resource.WithScope(resource.NamespacedScope)) kindRoutingTree = resource.Kind{ Schema: schemaRoutingTree, diff --git a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/templategroup_object_gen.go b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/templategroup_object_gen.go index 0866bcb258c..d755a887a3f 100644 --- a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/templategroup_object_gen.go +++ b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/templategroup_object_gen.go @@ -23,6 +23,12 @@ type TemplateGroup struct { Spec TemplateGroupSpec `json:"spec" yaml:"spec"` } +func NewTemplateGroup() *TemplateGroup { + return &TemplateGroup{ + Spec: *NewTemplateGroupSpec(), + } +} + func (o *TemplateGroup) GetSpec() any { return o.Spec } diff --git a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/templategroup_schema_gen.go b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/templategroup_schema_gen.go index 0be8cb1c6de..ba92e2c4c4c 100644 --- a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/templategroup_schema_gen.go +++ b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/templategroup_schema_gen.go @@ -10,7 +10,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( - schemaTemplateGroup = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", &TemplateGroup{}, &TemplateGroupList{}, resource.WithKind("TemplateGroup"), + schemaTemplateGroup = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", NewTemplateGroup(), &TemplateGroupList{}, resource.WithKind("TemplateGroup"), resource.WithPlural("templategroups"), resource.WithScope(resource.NamespacedScope)) kindTemplateGroup = resource.Kind{ Schema: schemaTemplateGroup, diff --git a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/timeinterval_object_gen.go b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/timeinterval_object_gen.go index 0ef813ee40c..e87b49dc958 100644 --- a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/timeinterval_object_gen.go +++ b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/timeinterval_object_gen.go @@ -23,6 +23,12 @@ type TimeInterval struct { Spec TimeIntervalSpec `json:"spec" yaml:"spec"` } +func NewTimeInterval() *TimeInterval { + return &TimeInterval{ + Spec: *NewTimeIntervalSpec(), + } +} + func (o *TimeInterval) GetSpec() any { return o.Spec } diff --git a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/timeinterval_schema_gen.go b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/timeinterval_schema_gen.go index 715bfbc0fe7..d342cb90637 100644 --- a/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/timeinterval_schema_gen.go +++ b/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1/timeinterval_schema_gen.go @@ -10,7 +10,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( - schemaTimeInterval = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", &TimeInterval{}, &TimeIntervalList{}, resource.WithKind("TimeInterval"), + schemaTimeInterval = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", NewTimeInterval(), &TimeIntervalList{}, resource.WithKind("TimeInterval"), resource.WithPlural("timeintervals"), resource.WithScope(resource.NamespacedScope)) kindTimeInterval = resource.Kind{ Schema: schemaTimeInterval, diff --git a/apps/alerting/rules/go.mod b/apps/alerting/rules/go.mod index 7286ccf5376..63da00536a6 100644 --- a/apps/alerting/rules/go.mod +++ b/apps/alerting/rules/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/alerting/rules go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 github.com/prometheus/common v0.67.3 k8s.io/apimachinery v0.34.2 diff --git a/apps/alerting/rules/go.sum b/apps/alerting/rules/go.sum index c447da4e325..3df8e8b5ebe 100644 --- a/apps/alerting/rules/go.sum +++ b/apps/alerting/rules/go.sum @@ -48,8 +48,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 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/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= diff --git a/apps/annotation/go.mod b/apps/annotation/go.mod index a042d852413..946c42fe38f 100644 --- a/apps/annotation/go.mod +++ b/apps/annotation/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/annotation go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 k8s.io/apimachinery v0.34.2 k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 diff --git a/apps/annotation/go.sum b/apps/annotation/go.sum index c447da4e325..3df8e8b5ebe 100644 --- a/apps/annotation/go.sum +++ b/apps/annotation/go.sum @@ -48,8 +48,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 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/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= diff --git a/apps/collections/go.mod b/apps/collections/go.mod index 81872ad4505..00575d7e6d4 100644 --- a/apps/collections/go.mod +++ b/apps/collections/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/collections go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 github.com/stretchr/testify v1.11.1 k8s.io/apimachinery v0.34.2 diff --git a/apps/collections/go.sum b/apps/collections/go.sum index 22bf6a8cbcd..75a19848d73 100644 --- a/apps/collections/go.sum +++ b/apps/collections/go.sum @@ -33,8 +33,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 h1:X0cnaFdR+iz+sDSuoZmkryFSjOirchHe2MdKSRwBWgM= diff --git a/apps/correlations/go.mod b/apps/correlations/go.mod index e1c9242aa14..29bc91e70a6 100644 --- a/apps/correlations/go.mod +++ b/apps/correlations/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/correlations go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 k8s.io/apimachinery v0.34.2 k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 diff --git a/apps/correlations/go.sum b/apps/correlations/go.sum index c447da4e325..3df8e8b5ebe 100644 --- a/apps/correlations/go.sum +++ b/apps/correlations/go.sum @@ -48,8 +48,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 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/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= diff --git a/apps/dashboard/go.mod b/apps/dashboard/go.mod index 4baf37a3657..0a19120fe80 100644 --- a/apps/dashboard/go.mod +++ b/apps/dashboard/go.mod @@ -5,7 +5,7 @@ go 1.25.5 require ( cuelang.org/go v0.11.1 github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 github.com/grafana/grafana-plugin-sdk-go v0.284.0 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e diff --git a/apps/dashboard/go.sum b/apps/dashboard/go.sum index 9c8cde10af6..0faeaaf78ba 100644 --- a/apps/dashboard/go.sum +++ b/apps/dashboard/go.sum @@ -85,8 +85,8 @@ github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 h1:Muoy+FMGr github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= 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.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana-plugin-sdk-go v0.284.0 h1:1bK7eWsnPBLUWDcWJWe218Ik5ad0a5JpEL4mH9ry7Ws= diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go index a267e0c8df8..ac8a0a61685 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_object_gen.go @@ -25,6 +25,13 @@ type Dashboard struct { Status DashboardStatus `json:"status" yaml:"status"` } +func NewDashboard() *Dashboard { + return &Dashboard{ + Spec: *NewDashboardSpec(), + Status: *NewDashboardStatus(), + } +} + func (o *Dashboard) GetSpec() any { return o.Spec } diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_schema_gen.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_schema_gen.go index 5b2da44ec05..1ec0884202f 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_schema_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_schema_gen.go @@ -10,7 +10,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( - schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v0alpha1", &Dashboard{}, &DashboardList{}, resource.WithKind("Dashboard"), + schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v0alpha1", NewDashboard(), &DashboardList{}, resource.WithKind("Dashboard"), resource.WithPlural("dashboards"), resource.WithScope(resource.NamespacedScope)) kindDashboard = resource.Kind{ Schema: schemaDashboard, diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/snapshot_object_gen.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/snapshot_object_gen.go index d917cebc0bf..64924eac264 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/snapshot_object_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/snapshot_object_gen.go @@ -23,6 +23,12 @@ type Snapshot struct { Spec SnapshotSpec `json:"spec" yaml:"spec"` } +func NewSnapshot() *Snapshot { + return &Snapshot{ + Spec: *NewSnapshotSpec(), + } +} + func (o *Snapshot) GetSpec() any { return o.Spec } diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/snapshot_schema_gen.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/snapshot_schema_gen.go index b6086c5fd1f..596c5bb2890 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/snapshot_schema_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/snapshot_schema_gen.go @@ -10,7 +10,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( - schemaSnapshot = resource.NewSimpleSchema("dashboard.grafana.app", "v0alpha1", &Snapshot{}, &SnapshotList{}, resource.WithKind("Snapshot"), + schemaSnapshot = resource.NewSimpleSchema("dashboard.grafana.app", "v0alpha1", NewSnapshot(), &SnapshotList{}, resource.WithKind("Snapshot"), resource.WithPlural("snapshots"), resource.WithScope(resource.NamespacedScope)) kindSnapshot = resource.Kind{ Schema: schemaSnapshot, diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_object_gen.go b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_object_gen.go index be021b5f003..35bb8900ab0 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_object_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_object_gen.go @@ -25,6 +25,13 @@ type Dashboard struct { Status DashboardStatus `json:"status" yaml:"status"` } +func NewDashboard() *Dashboard { + return &Dashboard{ + Spec: *NewDashboardSpec(), + Status: *NewDashboardStatus(), + } +} + func (o *Dashboard) GetSpec() any { return o.Spec } diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_schema_gen.go b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_schema_gen.go index e944e0afc33..006312837e0 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_schema_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_schema_gen.go @@ -10,7 +10,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( - schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v1beta1", &Dashboard{}, &DashboardList{}, resource.WithKind("Dashboard"), + schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v1beta1", NewDashboard(), &DashboardList{}, resource.WithKind("Dashboard"), resource.WithPlural("dashboards"), resource.WithScope(resource.NamespacedScope)) kindDashboard = resource.Kind{ Schema: schemaDashboard, diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_object_gen.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_object_gen.go index 99cf7df0da9..6a06594656e 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_object_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_object_gen.go @@ -25,6 +25,13 @@ type Dashboard struct { Status DashboardStatus `json:"status" yaml:"status"` } +func NewDashboard() *Dashboard { + return &Dashboard{ + Spec: *NewDashboardSpec(), + Status: *NewDashboardStatus(), + } +} + func (o *Dashboard) GetSpec() any { return o.Spec } diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_schema_gen.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_schema_gen.go index 136698cf70f..1a5f27d0fb5 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_schema_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_schema_gen.go @@ -10,7 +10,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( - schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v2alpha1", &Dashboard{}, &DashboardList{}, resource.WithKind("Dashboard"), + schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v2alpha1", NewDashboard(), &DashboardList{}, resource.WithKind("Dashboard"), resource.WithPlural("dashboards"), resource.WithScope(resource.NamespacedScope)) kindDashboard = resource.Kind{ Schema: schemaDashboard, diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_object_gen.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_object_gen.go index 5076b7d9b0c..bb64a321a1d 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_object_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_object_gen.go @@ -25,6 +25,13 @@ type Dashboard struct { Status DashboardStatus `json:"status" yaml:"status"` } +func NewDashboard() *Dashboard { + return &Dashboard{ + Spec: *NewDashboardSpec(), + Status: *NewDashboardStatus(), + } +} + func (o *Dashboard) GetSpec() any { return o.Spec } diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_schema_gen.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_schema_gen.go index 30c2237ca31..35d87fd07f7 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_schema_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_schema_gen.go @@ -10,7 +10,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( - schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v2beta1", &Dashboard{}, &DashboardList{}, resource.WithKind("Dashboard"), + schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v2beta1", NewDashboard(), &DashboardList{}, resource.WithKind("Dashboard"), resource.WithPlural("dashboards"), resource.WithScope(resource.NamespacedScope)) kindDashboard = resource.Kind{ Schema: schemaDashboard, diff --git a/apps/example/go.mod b/apps/example/go.mod index deb8763474c..d63d23be302 100644 --- a/apps/example/go.mod +++ b/apps/example/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/example go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20251017153501-8512b219c5fe k8s.io/apimachinery v0.34.2 diff --git a/apps/example/go.sum b/apps/example/go.sum index 4a43a44d46c..70c83a9fe37 100644 --- a/apps/example/go.sum +++ b/apps/example/go.sum @@ -56,8 +56,8 @@ github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 h1:Muoy+FMGr github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= 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.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20251017153501-8512b219c5fe h1:pPoFj2bQKDBg5EyEdOU+Jn+0hQN+M775Qihk73RbdSs= diff --git a/apps/folder/go.mod b/apps/folder/go.mod index c40b11f7add..476c6949c73 100644 --- a/apps/folder/go.mod +++ b/apps/folder/go.mod @@ -3,42 +3,67 @@ module github.com/grafana/grafana/apps/folder go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e k8s.io/apimachinery v0.34.2 k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 ) require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/getkin/kin-openapi v0.133.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-openapi/jsonpointer v0.22.1 // indirect github.com/go-openapi/jsonreference v0.21.2 // indirect github.com/go-openapi/swag v0.23.1 // indirect github.com/go-openapi/swag/jsonname v0.25.1 // indirect + github.com/go-test/deep v1.1.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/gnostic-models v0.7.0 // indirect + github.com/grafana/grafana-app-sdk/logging v0.48.3 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect + github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.3 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.11.1 // indirect + github.com/woodsbury/decimal128 v1.3.0 // indirect github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/net v0.47.0 // indirect + golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect + golang.org/x/time v0.14.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/client-go v0.34.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/apps/folder/go.sum b/apps/folder/go.sum index 286d9142c77..d8454185267 100644 --- a/apps/folder/go.sum +++ b/apps/folder/go.sum @@ -1,3 +1,7 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -6,6 +10,8 @@ github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bF github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ= +github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= @@ -16,6 +22,8 @@ github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZ github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU= github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= @@ -23,20 +31,33 @@ github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7O github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +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/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= +github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:IA4SOwun8QyST9c5UNs/fN37XL6boXXDvRYFcFwbipg= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -45,9 +66,29 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= +github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= +github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.3 h1:shd26MlnwTw5jksTDhC7rTQIteBxy+ZZDr3t7F2xN2Q= +github.com/prometheus/common v0.67.3/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= @@ -58,10 +99,20 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0= +github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -77,16 +128,24 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -100,12 +159,18 @@ google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= +k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= +k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= diff --git a/apps/folder/pkg/apis/folder/v1beta1/folder_object_gen.go b/apps/folder/pkg/apis/folder/v1beta1/folder_object_gen.go index 226af606f6f..6fabb8f8958 100644 --- a/apps/folder/pkg/apis/folder/v1beta1/folder_object_gen.go +++ b/apps/folder/pkg/apis/folder/v1beta1/folder_object_gen.go @@ -23,6 +23,12 @@ type Folder struct { Spec FolderSpec `json:"spec" yaml:"spec"` } +func NewFolder() *Folder { + return &Folder{ + Spec: *NewFolderSpec(), + } +} + func (o *Folder) GetSpec() any { return o.Spec } diff --git a/apps/folder/pkg/apis/folder/v1beta1/folder_schema_gen.go b/apps/folder/pkg/apis/folder/v1beta1/folder_schema_gen.go index f0d4fffe6b0..e626e4773ee 100644 --- a/apps/folder/pkg/apis/folder/v1beta1/folder_schema_gen.go +++ b/apps/folder/pkg/apis/folder/v1beta1/folder_schema_gen.go @@ -10,7 +10,7 @@ import ( // schema is unexported to prevent accidental overwrites var ( - schemaFolder = resource.NewSimpleSchema("folder.grafana.app", "v1beta1", &Folder{}, &FolderList{}, resource.WithKind("Folder"), + schemaFolder = resource.NewSimpleSchema("folder.grafana.app", "v1beta1", NewFolder(), &FolderList{}, resource.WithKind("Folder"), resource.WithPlural("folders"), resource.WithScope(resource.NamespacedScope)) kindFolder = resource.Kind{ Schema: schemaFolder, diff --git a/apps/folder/pkg/apis/manifestdata/folder_manifest.go b/apps/folder/pkg/apis/manifestdata/folder_manifest.go new file mode 100644 index 00000000000..7c053e52c38 --- /dev/null +++ b/apps/folder/pkg/apis/manifestdata/folder_manifest.go @@ -0,0 +1,116 @@ +// +// This file is generated by grafana-app-sdk +// DO NOT EDIT +// + +package manifestdata + +import ( + "fmt" + "strings" + + "github.com/grafana/grafana-app-sdk/app" + "github.com/grafana/grafana-app-sdk/resource" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/kube-openapi/pkg/spec3" + "k8s.io/kube-openapi/pkg/validation/spec" + + v1beta1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1" +) + +var appManifestData = app.ManifestData{ + AppName: "folder", + Group: "folder.grafana.app", + PreferredVersion: "v1beta1", + Versions: []app.ManifestVersion{ + { + Name: "v1beta1", + Served: true, + Kinds: []app.ManifestVersionKind{ + { + Kind: "Folder", + Plural: "Folders", + Scope: "Namespaced", + Conversion: false, + }, + }, + Routes: app.ManifestVersionRoutes{ + Namespaced: map[string]spec3.PathProps{}, + Cluster: map[string]spec3.PathProps{}, + Schemas: map[string]spec.Schema{}, + }, + }, + }, +} + +func LocalManifest() app.Manifest { + return app.NewEmbeddedManifest(appManifestData) +} + +func RemoteManifest() app.Manifest { + return app.NewAPIServerManifest("folder") +} + +var kindVersionToGoType = map[string]resource.Kind{ + "Folder/v1beta1": v1beta1.FolderKind(), +} + +// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists. +// If there is no association for the provided Kind and Version, exists will return false. +func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) { + goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)] + return goType, exists +} + +var customRouteToGoResponseType = map[string]any{} + +// ManifestCustomRouteResponsesAssociator returns the associated response go type for a given kind, version, custom route path, and method, if one exists. +// kind may be empty for custom routes which are not kind subroutes. Leading slashes are removed from subroute paths. +// If there is no association for the provided kind, version, custom route path, and method, exists will return false. +// Resource routes (those without a kind) should prefix their route with "/" if the route is namespaced (otherwise the route is assumed to be cluster-scope) +func ManifestCustomRouteResponsesAssociator(kind, version, path, verb string) (goType any, exists bool) { + if len(path) > 0 && path[0] == '/' { + path = path[1:] + } + goType, exists = customRouteToGoResponseType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))] + return goType, exists +} + +var customRouteToGoParamsType = map[string]runtime.Object{} + +func ManifestCustomRouteQueryAssociator(kind, version, path, verb string) (goType runtime.Object, exists bool) { + if len(path) > 0 && path[0] == '/' { + path = path[1:] + } + goType, exists = customRouteToGoParamsType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))] + return goType, exists +} + +var customRouteToGoRequestBodyType = map[string]any{} + +func ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb string) (goType any, exists bool) { + if len(path) > 0 && path[0] == '/' { + path = path[1:] + } + goType, exists = customRouteToGoRequestBodyType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))] + return goType, exists +} + +type GoTypeAssociator struct{} + +func NewGoTypeAssociator() *GoTypeAssociator { + return &GoTypeAssociator{} +} + +func (g *GoTypeAssociator) KindToGoType(kind, version string) (goType resource.Kind, exists bool) { + return ManifestGoTypeAssociator(kind, version) +} +func (g *GoTypeAssociator) CustomRouteReturnGoType(kind, version, path, verb string) (goType any, exists bool) { + return ManifestCustomRouteResponsesAssociator(kind, version, path, verb) +} +func (g *GoTypeAssociator) CustomRouteQueryGoType(kind, version, path, verb string) (goType runtime.Object, exists bool) { + return ManifestCustomRouteQueryAssociator(kind, version, path, verb) +} +func (g *GoTypeAssociator) CustomRouteRequestBodyGoType(kind, version, path, verb string) (goType any, exists bool) { + return ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb) +} diff --git a/apps/iam/go.mod b/apps/iam/go.mod index bfe1fa1c50d..474779e0efe 100644 --- a/apps/iam/go.mod +++ b/apps/iam/go.mod @@ -52,7 +52,7 @@ replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-aler require ( github.com/grafana/grafana v0.0.0-00010101000000-000000000000 - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 github.com/grafana/grafana/apps/folder v0.0.0 github.com/grafana/grafana/pkg/apimachinery v0.0.0 diff --git a/apps/iam/go.sum b/apps/iam/go.sum index 4c25bd92d43..7f2fd8f462f 100644 --- a/apps/iam/go.sum +++ b/apps/iam/go.sum @@ -835,8 +835,8 @@ github.com/grafana/gofpdf v0.0.0-20250307124105-3b9c5d35577f h1:5xkjl5Y/j2QefJKO github.com/grafana/gofpdf v0.0.0-20250307124105-3b9c5d35577f/go.mod h1:+O5QxOwwgP10jedZHapzXY+IPKTnzHBtIs5UUb9G+kI= github.com/grafana/gomemcache v0.0.0-20250828162811-a96f6acee2fe h1:q+QaVANzNZxvTovycpQvDTfsNZ2rHh4XIIaccMnrIR4= github.com/grafana/gomemcache v0.0.0-20250828162811-a96f6acee2fe/go.mod h1:j/s0jkda4UXTemDs7Pgw/vMT06alWc42CHisvYac0qw= -github.com/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana-aws-sdk v1.3.0 h1:/bfJzP93rCel1GbWoRSq0oUo424MZXt8jAp2BK9w8tM= diff --git a/apps/investigations/go.mod b/apps/investigations/go.mod index 0faf2c40efb..3844fec2689 100644 --- a/apps/investigations/go.mod +++ b/apps/investigations/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/investigations go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 k8s.io/apimachinery v0.34.2 k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 diff --git a/apps/investigations/go.sum b/apps/investigations/go.sum index c447da4e325..3df8e8b5ebe 100644 --- a/apps/investigations/go.sum +++ b/apps/investigations/go.sum @@ -48,8 +48,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 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/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= diff --git a/apps/logsdrilldown/go.mod b/apps/logsdrilldown/go.mod index b70fb8a42f5..2a35278d19a 100644 --- a/apps/logsdrilldown/go.mod +++ b/apps/logsdrilldown/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/logsdrilldown go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 k8s.io/apimachinery v0.34.2 k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 diff --git a/apps/logsdrilldown/go.sum b/apps/logsdrilldown/go.sum index c447da4e325..3df8e8b5ebe 100644 --- a/apps/logsdrilldown/go.sum +++ b/apps/logsdrilldown/go.sum @@ -48,8 +48,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 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/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod index 4bdbe8c003c..ae38e3d0cd3 100644 --- a/apps/playlist/go.mod +++ b/apps/playlist/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/playlist go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 k8s.io/apimachinery v0.34.2 k8s.io/client-go v0.34.2 k8s.io/klog/v2 v2.130.1 diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum index c447da4e325..3df8e8b5ebe 100644 --- a/apps/playlist/go.sum +++ b/apps/playlist/go.sum @@ -48,8 +48,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 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/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= diff --git a/apps/plugins/go.mod b/apps/plugins/go.mod index c55674cc950..669c7c46844 100644 --- a/apps/plugins/go.mod +++ b/apps/plugins/go.mod @@ -10,8 +10,9 @@ replace github.com/grafana/grafana/pkg/apiserver => ../../pkg/apiserver require ( github.com/emicklei/go-restful/v3 v3.13.0 + github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 github.com/grafana/grafana v0.0.0-00010101000000-000000000000 - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 github.com/grafana/grafana/pkg/apimachinery v0.0.0 github.com/stretchr/testify v1.11.1 @@ -59,7 +60,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.22.1 // indirect github.com/go-openapi/jsonreference v0.21.2 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect github.com/go-openapi/swag/jsonname v0.25.1 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-test/deep v1.1.1 // indirect @@ -75,9 +76,8 @@ require ( github.com/google/gnostic-models v0.7.0 // 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-20251119204204-77fa75125181 // indirect + github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba // 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 github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect github.com/grafana/grafana-aws-sdk v1.3.0 // indirect @@ -142,7 +142,7 @@ require ( github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/alertmanager v0.28.0 // indirect + github.com/prometheus/alertmanager v0.28.2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.3 // indirect @@ -194,8 +194,8 @@ require ( golang.org/x/tools v0.39.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect google.golang.org/grpc v1.77.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect @@ -215,3 +215,6 @@ require ( sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) + +// Use our fork of the upstream Alertmanager. +replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-alertmanager v0.25.1-0.20250911094103-5456b6e45604 diff --git a/apps/plugins/go.sum b/apps/plugins/go.sum index d582270e43b..1d7387b28b3 100644 --- a/apps/plugins/go.sum +++ b/apps/plugins/go.sum @@ -110,8 +110,8 @@ github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM= github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU= github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU= github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= @@ -174,8 +174,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-20251119204204-77fa75125181 h1:nbxKRtrbuhvOYmI2RhOYauHRJCtpR+vTNIgg1lFUCws= -github.com/grafana/alerting v0.0.0-20251119204204-77fa75125181/go.mod h1:VtPNIFlEOJPPEc13Ax6ZTbNV3M/sAzLID72YjgzOPVA= +github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba h1:psKWNETD5nGxmFAlqnWsXoRyUwSa2GHNEMSEDKGKfQ4= +github.com/grafana/alerting v0.0.0-20251204145817-de8c2bbf9eba/go.mod h1:l7v67cgP7x72ajB9UPZlumdrHqNztpKoqQ52cU8T3LU= 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= @@ -184,8 +184,8 @@ github.com/grafana/dataplane/sdata v0.0.9 h1:AGL1LZnCUG4MnQtnWpBPbQ8ZpptaZs14w6k github.com/grafana/dataplane/sdata v0.0.9/go.mod h1:Jvs5ddpGmn6vcxT7tCTWAZ1mgi4sbcdFt9utQx5uMAU= 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.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana-aws-sdk v1.3.0 h1:/bfJzP93rCel1GbWoRSq0oUo424MZXt8jAp2BK9w8tM= @@ -196,6 +196,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.284.0 h1:1bK7eWsnPBLUWDcWJWe218Ik5ad github.com/grafana/grafana-plugin-sdk-go v0.284.0/go.mod h1:lHPniaSxq3SL5MxDIPy04TYB1jnTp/ivkYO+xn5Rz3E= github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8= github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls= +github.com/grafana/prometheus-alertmanager v0.25.1-0.20250911094103-5456b6e45604 h1:aXfUhVN/Ewfpbko2CCtL65cIiGgwStOo4lWH2b6gw2U= +github.com/grafana/prometheus-alertmanager v0.25.1-0.20250911094103-5456b6e45604/go.mod h1:O/QP1BCm0HHIzbKvgMzqb5sSyH88rzkFk84F4TfJjBU= github.com/grafana/pyroscope-go/godeltaprof v0.1.9 h1:c1Us8i6eSmkW+Ez05d3co8kasnuOY813tbMN8i/a3Og= github.com/grafana/pyroscope-go/godeltaprof v0.1.9/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/grafana/sqlds/v4 v4.2.7 h1:sFQhsS7DBakNMdxa++yOfJ9BVvkZwFJ0B95o57K0/XA= @@ -368,8 +370,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/alertmanager v0.28.0 h1:sLN+6HhZet8hrbmGHLAHWsTXgZSVCvq9Ix3U3wvivqc= -github.com/prometheus/alertmanager v0.28.0/go.mod h1:/okSnb2LlodbMlRoOWQEKtqI/coOo2NKZDm2Hu9QHLQ= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -611,10 +611,10 @@ gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4= -google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:B14OtaXuMaCQsl2deSvNkyPKIzq3BjfxQp8d00QyWx4= +google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= diff --git a/apps/preferences/go.mod b/apps/preferences/go.mod index 9a5c8088c0f..661002a3b59 100644 --- a/apps/preferences/go.mod +++ b/apps/preferences/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/preferences go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 k8s.io/apimachinery v0.34.2 k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 diff --git a/apps/preferences/go.sum b/apps/preferences/go.sum index 22bf6a8cbcd..75a19848d73 100644 --- a/apps/preferences/go.sum +++ b/apps/preferences/go.sum @@ -33,8 +33,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250804150913-990f1c69ecc2 h1:X0cnaFdR+iz+sDSuoZmkryFSjOirchHe2MdKSRwBWgM= diff --git a/apps/provisioning/go.mod b/apps/provisioning/go.mod index e84ab0d35da..3cccd7dd8be 100644 --- a/apps/provisioning/go.mod +++ b/apps/provisioning/go.mod @@ -44,7 +44,7 @@ require ( github.com/gorilla/mux v1.8.1 // indirect github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 // indirect github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect - github.com/grafana/grafana-app-sdk v0.48.4 // indirect + github.com/grafana/grafana-app-sdk v0.48.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.18.0 // indirect diff --git a/apps/provisioning/go.sum b/apps/provisioning/go.sum index 791bf68e797..0e1ba180fad 100644 --- a/apps/provisioning/go.sum +++ b/apps/provisioning/go.sum @@ -62,8 +62,8 @@ github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 h1:Muoy+FMGr github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= 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.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana/apps/secret v0.0.0-20250902093454-b56b7add012f h1:f+Z5Xpfp1WNYjUe23ginerWsHWUsRgOWrr3WGu3SlWs= diff --git a/apps/sdk.mk b/apps/sdk.mk index 62d89ed8ed3..68cf4c1c9c9 100644 --- a/apps/sdk.mk +++ b/apps/sdk.mk @@ -1,4 +1,4 @@ -APP_SDK_VERSION = v0.48.4 +APP_SDK_VERSION = v0.48.5 APP_SDK_DIR = $(shell go env GOPATH)/bin/app-sdk-$(APP_SDK_VERSION) APP_SDK_BIN = $(APP_SDK_DIR)/grafana-app-sdk diff --git a/apps/secret/go.mod b/apps/secret/go.mod index da71ef8e666..a1ed48be6e7 100644 --- a/apps/secret/go.mod +++ b/apps/secret/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/secret go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250710134100-1f3dc0533caf github.com/stretchr/testify v1.11.1 go.yaml.in/yaml/v3 v3.0.4 diff --git a/apps/secret/go.sum b/apps/secret/go.sum index 950843f4a7d..166e281f5af 100644 --- a/apps/secret/go.sum +++ b/apps/secret/go.sum @@ -37,8 +37,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250710134100-1f3dc0533caf h1:BBGDHffvVNLoYQlXEpbXcxE0vbpq7pm/8OWF5I+UDZg= diff --git a/apps/shorturl/go.mod b/apps/shorturl/go.mod index 128609888ba..4a414de5b4e 100644 --- a/apps/shorturl/go.mod +++ b/apps/shorturl/go.mod @@ -3,7 +3,7 @@ module github.com/grafana/grafana/apps/shorturl go 1.25.5 require ( - github.com/grafana/grafana-app-sdk v0.48.4 + github.com/grafana/grafana-app-sdk v0.48.5 github.com/grafana/grafana-app-sdk/logging v0.48.3 github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250915132226-585b53bc7dba k8s.io/apimachinery v0.34.2 diff --git a/apps/shorturl/go.sum b/apps/shorturl/go.sum index c7f57094324..58ecc31fd82 100644 --- a/apps/shorturl/go.sum +++ b/apps/shorturl/go.sum @@ -56,8 +56,8 @@ github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4 h1:Muoy+FMGr github.com/grafana/authlib/types v0.0.0-20251119142549-be091cf2f4d4/go.mod h1:qeWYbnWzaYGl88JlL9+DsP1GT2Cudm58rLtx13fKZdw= 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.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250915132226-585b53bc7dba h1:Qam8QzVRsyZN39zgZ9Vj6e8PEfswvv2McnqCZ/v5NcI= diff --git a/go.mod b/go.mod index 7ef730e391d..e589e5a18a5 100644 --- a/go.mod +++ b/go.mod @@ -97,7 +97,7 @@ require ( github.com/grafana/gofpdf v0.0.0-20250307124105-3b9c5d35577f // @grafana/sharing-squad github.com/grafana/gomemcache v0.0.0-20250318131618-74242eea118d // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-api-golang-client v0.27.0 // @grafana/alerting-backend - github.com/grafana/grafana-app-sdk v0.48.4 // @grafana/grafana-app-platform-squad + github.com/grafana/grafana-app-sdk v0.48.5 // @grafana/grafana-app-platform-squad github.com/grafana/grafana-app-sdk/logging v0.48.3 // @grafana/grafana-app-platform-squad github.com/grafana/grafana-aws-sdk v1.3.0 // @grafana/aws-datasources github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1 // @grafana/partner-datasources diff --git a/go.sum b/go.sum index 7723cfa19a9..91104bfeaf4 100644 --- a/go.sum +++ b/go.sum @@ -1635,8 +1635,8 @@ github.com/grafana/gomemcache v0.0.0-20250318131618-74242eea118d h1:oXRJlb9UjVsl github.com/grafana/gomemcache v0.0.0-20250318131618-74242eea118d/go.mod h1:j/s0jkda4UXTemDs7Pgw/vMT06alWc42CHisvYac0qw= github.com/grafana/grafana-api-golang-client v0.27.0 h1:zIwMXcbCB4n588i3O2N6HfNcQogCNTd/vPkEXTr7zX8= github.com/grafana/grafana-api-golang-client v0.27.0/go.mod h1:uNLZEmgKtTjHBtCQMwNn3qsx2mpMb8zU+7T4Xv3NR9Y= -github.com/grafana/grafana-app-sdk v0.48.4 h1:t9r+Y6E7D832ZxQ2c1n0lp6cvsYKhhrAodVYzE1y0s0= -github.com/grafana/grafana-app-sdk v0.48.4/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= +github.com/grafana/grafana-app-sdk v0.48.5 h1:MS8l9fTZz+VbTfgApn09jw27GxhQ6fNOWGhC4ydvZmM= +github.com/grafana/grafana-app-sdk v0.48.5/go.mod h1:HJsMOSBmt/D/Ihs1SvagOwmXKi0coBMVHlfvdd+qe9Y= github.com/grafana/grafana-app-sdk/logging v0.48.3 h1:72NUpGNiJXCNQz/on++YSsl38xuVYYBKv5kKQaOClX4= github.com/grafana/grafana-app-sdk/logging v0.48.3/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana-aws-sdk v1.3.0 h1:/bfJzP93rCel1GbWoRSq0oUo424MZXt8jAp2BK9w8tM= diff --git a/go.work.sum b/go.work.sum index 36bd9f3510d..3017d8eb878 100644 --- a/go.work.sum +++ b/go.work.sum @@ -775,7 +775,6 @@ github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5 github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-pdf/fpdf v0.6.0 h1:MlgtGIfsdMEEQJr2le6b/HNr1ZlQwxyWr77r2aj2U/8= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= @@ -2084,6 +2083,7 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go. google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= google.golang.org/genproto/googleapis/api v0.0.0-20250908214217-97024824d090/go.mod h1:U8EXRNSd8sUYyDfs/It7KVWodQr+Hf9xtxyxWudSwEw= google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4/go.mod h1:NnuHhy+bxcg30o7FnVAZbXsPHUDQ9qKWAQKCD7VxFtk= +google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822 h1:zWFRixYR5QlotL+Uv3YfsPRENIrQFXiGs+iwqel6fOQ= google.golang.org/genproto/googleapis/bytestream v0.0.0-20250603155806-513f23925822/go.mod h1:h6yxum/C2qRb4txaZRLDHK8RyS0H/o2oEDeKY4onY/Y= google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= @@ -2114,6 +2114,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= From 8e11851bb085fac6ce2438d4b7dd5d340100ce48 Mon Sep 17 00:00:00 2001 From: Austin Pond Date: Sat, 6 Dec 2025 03:01:28 -0500 Subject: [PATCH 34/48] =?UTF-8?q?Dashboards:=20Use=20the=20OpenAPI=20gener?= =?UTF-8?q?ated=20by=20app-sdk=20in=20the=20manifest=20to=20=E2=80=A6=20(#?= =?UTF-8?q?114858)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dashboard/Makefile | 3 +- apps/dashboard/pkg/apis/dashboard_manifest.go | 24 + pkg/registry/apis/dashboard/register.go | 45 + .../dashboard.grafana.app-v2alpha1.json | 2061 ++++---- .../dashboard.grafana.app-v2beta1.json | 4591 +++++++++++++++++ pkg/tests/apis/openapi_test.go | 3 + 6 files changed, 5552 insertions(+), 1175 deletions(-) create mode 100644 pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2beta1.json diff --git a/apps/dashboard/Makefile b/apps/dashboard/Makefile index 3d5c7060199..7ff9b946dc9 100644 --- a/apps/dashboard/Makefile +++ b/apps/dashboard/Makefile @@ -11,8 +11,7 @@ do-generate: install-app-sdk update-app-sdk ## Run Grafana App SDK code generati --tsgenpath=../../packages/grafana-schema/src/schema \ --grouping=group \ --defencoding=none \ - --genoperatorstate=false \ - --noschemasinmanifest + --genoperatorstate=false .PHONY: post-generate-cleanup post-generate-cleanup: ## Clean up the generated code diff --git a/apps/dashboard/pkg/apis/dashboard_manifest.go b/apps/dashboard/pkg/apis/dashboard_manifest.go index 974062efcec..c4e35bd8f40 100644 --- a/apps/dashboard/pkg/apis/dashboard_manifest.go +++ b/apps/dashboard/pkg/apis/dashboard_manifest.go @@ -6,6 +6,7 @@ package apis import ( + "encoding/json" "fmt" "strings" @@ -21,6 +22,24 @@ import ( v2beta1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1" ) +var ( + rawSchemaDashboardv0alpha1 = []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"}}`) + versionSchemaDashboardv0alpha1 app.VersionSchema + _ = json.Unmarshal(rawSchemaDashboardv0alpha1, &versionSchemaDashboardv0alpha1) + rawSchemaSnapshotv0alpha1 = []byte(`{"Snapshot":{"properties":{"spec":{"$ref":"#/components/schemas/spec"}},"required":["spec"]},"spec":{"additionalProperties":false,"properties":{"dashboard":{"additionalProperties":{"additionalProperties":{},"type":"object"},"description":"The raw dashboard (unstructured for now)","type":"object"},"expires":{"default":0,"description":"Optionally auto-remove the snapshot at a future date (Unix timestamp in seconds)","type":"integer"},"external":{"default":false,"description":"When set to true, the snapshot exists in a remote server","type":"boolean"},"externalUrl":{"description":"The external URL where the snapshot can be seen","type":"string"},"originalUrl":{"description":"The URL that created the dashboard originally","type":"string"},"timestamp":{"description":"Snapshot creation timestamp","type":"string"},"title":{"description":"Snapshot title","type":"string"}},"type":"object"}}`) + versionSchemaSnapshotv0alpha1 app.VersionSchema + _ = json.Unmarshal(rawSchemaSnapshotv0alpha1, &versionSchemaSnapshotv0alpha1) + 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"}},"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"},"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"},"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"}},"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"},"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"},"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) +) + var appManifestData = app.ManifestData{ AppName: "dashboard", Group: "dashboard.grafana.app", @@ -35,6 +54,7 @@ var appManifestData = app.ManifestData{ Plural: "Dashboards", Scope: "Namespaced", Conversion: false, + Schema: &versionSchemaDashboardv0alpha1, }, { @@ -42,6 +62,7 @@ var appManifestData = app.ManifestData{ Plural: "Snapshots", Scope: "Namespaced", Conversion: false, + Schema: &versionSchemaSnapshotv0alpha1, }, }, Routes: app.ManifestVersionRoutes{ @@ -60,6 +81,7 @@ var appManifestData = app.ManifestData{ Plural: "Dashboards", Scope: "Namespaced", Conversion: false, + Schema: &versionSchemaDashboardv1beta1, }, }, Routes: app.ManifestVersionRoutes{ @@ -78,6 +100,7 @@ var appManifestData = app.ManifestData{ Plural: "Dashboards", Scope: "Namespaced", Conversion: false, + Schema: &versionSchemaDashboardv2alpha1, }, }, Routes: app.ManifestVersionRoutes{ @@ -96,6 +119,7 @@ var appManifestData = app.ManifestData{ Plural: "Dashboards", Scope: "Namespaced", Conversion: false, + Schema: &versionSchemaDashboardv2beta1, }, }, Routes: app.ManifestVersionRoutes{ diff --git a/pkg/registry/apis/dashboard/register.go b/pkg/registry/apis/dashboard/register.go index 16873ec2101..e9925f0a3b7 100644 --- a/pkg/registry/apis/dashboard/register.go +++ b/pkg/registry/apis/dashboard/register.go @@ -24,6 +24,7 @@ import ( authlib "github.com/grafana/authlib/types" "github.com/grafana/grafana-app-sdk/logging" + manifestdata "github.com/grafana/grafana/apps/dashboard/pkg/apis" internal "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard" dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1" dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1" @@ -843,6 +844,50 @@ func (b *DashboardsAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefiniti maps.Copy(defs, dashv1.GetOpenAPIDefinitions(ref)) maps.Copy(defs, dashv2alpha1.GetOpenAPIDefinitions(ref)) maps.Copy(defs, dashv2beta1.GetOpenAPIDefinitions(ref)) + md := manifestdata.LocalManifest().ManifestData + // Overwrite the OpenAPI generated from kubernetes (sourced from the go types) with the OpenAPI generated by grafana-app-sdk + // from the manifest CUE, as it correctly handles the CUE disjunctions in the dashboard spec. + // We don't touch any types which were not specified in the manifest CUE (such as custom route types). + for _, version := range md.Versions { + // We don't need to correct the v0 or v1 openAPI as the spec type is just `any` + if len(version.Name) > 1 && (version.Name[1] == '0' || version.Name[1] == '1') { + continue + } + for _, kind := range version.Kinds { + pkgPrefix := fmt.Sprintf("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/%s", version.Name) + oapi, err := kind.Schema.AsKubeOpenAPI(schema.GroupVersionKind{ + Group: md.Group, + Version: version.Name, + Kind: kind.Kind, + }, ref, pkgPrefix) + if err != nil { + logging.DefaultLogger.Error("unable to generate openAPI for kind %s: %w", kind.Kind, err) + continue + } + maps.Copy(defs, oapi) + } + } + + // Fix legacyOptions schema for v2alpha1 and v2beta1 to allow any value type + // The generated schema incorrectly restricts values to objects, but map[string]interface{} can hold any type + // This fix must be applied here so structured-merge-diff uses the correct schema + // For some reason this issue occurs with both the kubernetes-generated openAPI sourced from go, _and_ the OpenAPI from the AppManifest + // TODO: @IfSentient this should really be addressed in the app-sdk's generation, or work out what about this particular CUE value is broken + for _, defKey := range []string{ + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardAnnotationQuerySpec", + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAnnotationQuerySpec", + } { + if def, ok := defs[defKey]; ok { + if legacyOptions, ok := def.Schema.Properties["legacyOptions"]; ok { + // Fix: Use additionalProperties: true to allow any value type (string, number, boolean, array, object, etc.) + // instead of restricting to objects only. This must match map[string]interface{} semantics. + legacyOptions.AdditionalProperties = &spec.SchemaOrBool{Allows: true} + def.Schema.Properties["legacyOptions"] = legacyOptions + defs[defKey] = def + } + } + } + return defs } } 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 567636ff7fc..2cad6213d04 100644 --- a/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2alpha1.json @@ -968,9 +968,10 @@ "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.Dashboard": { "type": "object", "required": [ + "kind", + "apiVersion", "metadata", - "spec", - "status" + "spec" ], "properties": { "apiVersion": { @@ -990,21 +991,10 @@ ] }, "spec": { - "description": "Spec is the spec of the Dashboard", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSpec" }, "status": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardStatus" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardStatus" } }, "x-kubernetes-group-version-kind": [ @@ -1085,28 +1075,35 @@ "type": "boolean" }, "style": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1ActionStyle" + "type": "object", + "properties": { + "backgroundColor": { + "type": "string" + } + }, + "additionalProperties": false }, "title": { - "type": "string", - "default": "" + "type": "string" }, "type": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardActionType" }, "variables": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardActionVariable" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardActionVariable" } } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardActionType": { + "type": "string", + "enum": [ + "fetch", + "infinity" + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardActionVariable": { "type": "object", @@ -1117,18 +1114,20 @@ ], "properties": { "key": { - "type": "string", - "default": "" + "type": "string" }, "name": { - "type": "string", - "default": "" + "type": "string" }, "type": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardActionVariableType" } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardActionVariableType": { + "description": "Action variable type", + "type": "string" }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdHocFilterWithLabels": { "description": "Define the AdHocFilterWithLabels type", @@ -1147,38 +1146,34 @@ "type": "boolean" }, "key": { - "type": "string", - "default": "" + "type": "string" }, "keyLabel": { "type": "string" }, "operator": { - "type": "string", - "default": "" - }, - "origin": { "type": "string" }, + "origin": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFilterOrigin" + }, "value": { - "type": "string", - "default": "" + "type": "string" }, "valueLabels": { "type": "array", "items": { - "type": "string", - "default": "" + "type": "string" } }, "values": { "type": "array", "items": { - "type": "string", - "default": "" + "type": "string" } } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdhocVariableKind": { "description": "Adhoc variable kind", @@ -1189,18 +1184,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdhocVariableSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdhocVariableSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdhocVariableSpec": { "description": "Adhoc variable specification", @@ -1217,17 +1207,12 @@ "properties": { "allowCustomValue": { "type": "boolean", - "default": false + "default": true }, "baseFilters": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdHocFilterWithLabels" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdHocFilterWithLabels" } }, "datasource": { @@ -1236,12 +1221,7 @@ "defaultKeys": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMetricFindValue" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMetricFindValue" } }, "description": { @@ -1250,17 +1230,11 @@ "filters": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdHocFilterWithLabels" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdHocFilterWithLabels" } }, "hide": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableHide" }, "label": { "type": "string" @@ -1273,7 +1247,8 @@ "type": "boolean", "default": false } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationEventFieldMapping": { "description": "Annotation event field mapping. Defines how to map a data frame field to an annotation event field.", @@ -1285,13 +1260,15 @@ }, "source": { "description": "Source type for the field value", - "type": "string" + "type": "string", + "default": "field" }, "value": { "description": "Constant value to use when source is \"text\"", "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationPanelFilter": { "type": "object", @@ -1301,18 +1278,18 @@ "properties": { "exclude": { "description": "Should the specified panels be included or excluded", - "type": "boolean" + "type": "boolean", + "default": false }, "ids": { "description": "Panel IDs that should be included or excluded", "type": "array", "items": { - "type": "integer", - "format": "int64", - "default": 0 + "type": "integer" } } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationQueryKind": { "type": "object", @@ -1322,18 +1299,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationQuerySpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationQuerySpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationQuerySpec": { "type": "object", @@ -1345,53 +1317,44 @@ ], "properties": { "builtIn": { - "type": "boolean" + "type": "boolean", + "default": false }, "datasource": { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataSourceRef" }, "enable": { - "type": "boolean", - "default": false + "type": "boolean" }, "filter": { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationPanelFilter" }, "hide": { - "type": "boolean", - "default": false + "type": "boolean" }, "iconColor": { - "type": "string", - "default": "" + "type": "string" }, "legacyOptions": { "description": "Catch-all field for datasource-specific properties", "type": "object", - "additionalProperties": { - "type": "object" - } + "additionalProperties": true }, "mappings": { "description": "Mappings define how to convert data frame fields to annotation event fields.", "type": "object", "additionalProperties": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationEventFieldMapping" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationEventFieldMapping" } }, "name": { - "type": "string", - "default": "" + "type": "string" }, "query": { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataQueryKind" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutItemKind": { "type": "object", @@ -1401,18 +1364,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutItemSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutItemSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutItemSpec": { "type": "object", @@ -1424,17 +1382,13 @@ "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingGroupKind" }, "element": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardElementReference" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardElementReference" }, "repeat": { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridRepeatOptions" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutKind": { "type": "object", @@ -1444,18 +1398,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutSpec": { "type": "object", @@ -1466,40 +1415,47 @@ ], "properties": { "columnWidth": { - "type": "number", - "format": "double" + "type": "number" }, "columnWidthMode": { "type": "string", - "default": "" + "default": "standard", + "enum": [ + "narrow", + "standard", + "wide", + "custom" + ] }, "fillScreen": { - "type": "boolean" + "type": "boolean", + "default": false }, "items": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutItemKind" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutItemKind" } }, "maxColumnCount": { "type": "number", - "format": "double" + "default": 3 }, "rowHeight": { - "type": "number", - "format": "double" + "type": "number" }, "rowHeightMode": { "type": "string", - "default": "" + "default": "standard", + "enum": [ + "short", + "standard", + "tall", + "custom" + ] } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridRepeatOptions": { "type": "object", @@ -1509,14 +1465,13 @@ ], "properties": { "mode": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRepeatMode" }, "value": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingDataKind": { "type": "object", @@ -1526,18 +1481,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingDataSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingDataSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingDataSpec": { "type": "object", @@ -1546,10 +1496,10 @@ ], "properties": { "value": { - "type": "boolean", - "default": false + "type": "boolean" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingGroupKind": { "type": "object", @@ -1559,18 +1509,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingGroupSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingGroupSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingGroupSpec": { "type": "object", @@ -1582,19 +1527,36 @@ "properties": { "condition": { "type": "string", - "default": "" + "enum": [ + "and", + "or" + ] }, "items": { "type": "array", "items": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind" + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingDataKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingTimeRangeSizeKind" + } + ] } }, "visibility": { "type": "string", - "default": "" + "enum": [ + "show", + "hide" + ] } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingTimeRangeSizeKind": { "type": "object", @@ -1604,18 +1566,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingTimeRangeSizeSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingTimeRangeSizeSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingTimeRangeSizeSpec": { "type": "object", @@ -1624,10 +1581,10 @@ ], "properties": { "value": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingVariableKind": { "type": "object", @@ -1637,32 +1594,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingVariableSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingVariableSpec" } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind": { - "type": "object", - "properties": { - "ConditionalRenderingDataKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingDataKind" - }, - "ConditionalRenderingTimeRangeSizeKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingTimeRangeSizeKind" - }, - "ConditionalRenderingVariableKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingVariableKind" - } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingVariableSpec": { "type": "object", @@ -1674,17 +1612,21 @@ "properties": { "operator": { "type": "string", - "default": "" + "enum": [ + "equals", + "notEquals", + "matches", + "notMatches" + ] }, "value": { - "type": "string", - "default": "" + "type": "string" }, "variable": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConstantVariableKind": { "description": "Constant variable kind", @@ -1695,18 +1637,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConstantVariableSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConstantVariableSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConstantVariableSpec": { "description": "Constant variable specification", @@ -1720,19 +1657,13 @@ ], "properties": { "current": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" }, "description": { "type": "string" }, "hide": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableHide" }, "label": { "type": "string" @@ -1749,7 +1680,8 @@ "type": "boolean", "default": false } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConversionStatus": { "description": "ConversionStatus is the status of the conversion of the dashboard.", @@ -1759,23 +1691,24 @@ ], "properties": { "error": { - "description": "The error message from the conversion. Empty if the conversion has not failed.", + "description": "The error message from the conversion.\nEmpty if the conversion has not failed.", "type": "string" }, "failed": { - "description": "Whether from another version has failed. If true, means that the dashboard is not valid, and the caller should instead fetch the stored version.", - "type": "boolean", - "default": false + "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": { "description": "The original value map[string]any", - "type": "object" + "type": "object", + "additionalProperties": {} }, "storedVersion": { - "description": "The version which was stored when the dashboard was created / updated. Fetching this version should always succeed.", + "description": "The version which was stored when the dashboard was created / updated.\nFetching this version should always succeed.", "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardCustomVariableKind": { "description": "Custom variable kind", @@ -1786,18 +1719,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardCustomVariableSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardCustomVariableSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardCustomVariableSpec": { "description": "Custom variable specification", @@ -1819,22 +1747,16 @@ }, "allowCustomValue": { "type": "boolean", - "default": false + "default": true }, "current": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" }, "description": { "type": "string" }, "hide": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableHide" }, "includeAll": { "type": "boolean", @@ -1854,12 +1776,7 @@ "options": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" } }, "query": { @@ -1870,7 +1787,17 @@ "type": "boolean", "default": false } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDashboardCursorSync": { + "description": "\"Off\" for no shared crosshair or tooltip (default).\n\"Crosshair\" for shared crosshair.\n\"Tooltip\" for shared crosshair AND shared tooltip.", + "type": "string", + "enum": [ + "Crosshair", + "Tooltip", + "Off" + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDashboardLink": { "description": "Links with references to other dashboards or external resources", @@ -1894,8 +1821,7 @@ }, "icon": { "description": "Icon name to be displayed with the link", - "type": "string", - "default": "" + "type": "string" }, "includeVars": { "description": "If true, includes current template variables values in the link as query params", @@ -1908,15 +1834,13 @@ "default": false }, "placement": { - "description": "Placement can be used to display the link somewhere else on the dashboard other than above the visualisations.", - "type": "string" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDashboardLinkPlacement" }, "tags": { "description": "List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards", "type": "array", "items": { - "type": "string", - "default": "" + "type": "string" } }, "targetBlank": { @@ -1926,24 +1850,33 @@ }, "title": { "description": "Title to display with the link", - "type": "string", - "default": "" + "type": "string" }, "tooltip": { "description": "Tooltip to display when the user hovers their mouse over it", - "type": "string", - "default": "" + "type": "string" }, "type": { - "description": "Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource) FIXME: The type is generated as `type: DashboardLinkType | dashboardLinkType.Link;` but it should be `type: DashboardLinkType`", - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDashboardLinkType" }, "url": { "description": "Link URL. Only required/valid if the type is link", "type": "string" } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDashboardLinkPlacement": { + "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" + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDashboardLinkType": { + "description": "Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)", + "type": "string", + "enum": [ + "link", + "dashboards" + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataLink": { "type": "object", @@ -1956,14 +1889,13 @@ "type": "boolean" }, "title": { - "type": "string", - "default": "" + "type": "string" }, "url": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataQueryKind": { "type": "object", @@ -1974,16 +1906,17 @@ "properties": { "kind": { "description": "The kind of a DataQueryKind is the datasource type", - "type": "string", - "default": "" + "type": "string" }, "spec": { "type": "object", "additionalProperties": { - "type": "object" + "type": "object", + "additionalProperties": {} } } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataSourceRef": { "type": "object", @@ -1996,10 +1929,20 @@ "description": "Specific datasource instance", "type": "string" } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataTopic": { + "description": "A topic is attached to DataFrame metadata in query results.\nThis specifies where the data should be used.", + "type": "string", + "enum": [ + "series", + "annotations", + "alertStates" + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataTransformerConfig": { - "description": "Transformations allow 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, use the output of one transformation as the input to another transformation, etc.", + "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.", "type": "object", "required": [ "id", @@ -2011,27 +1954,22 @@ "type": "boolean" }, "filter": { - "description": "Optional frame matcher. When missing it will be applied to all results", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMatcherConfig" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMatcherConfig" }, "id": { "description": "Unique identifier of transformer", - "type": "string", - "default": "" + "type": "string" }, "options": { - "description": "Options to be passed to the transformer Valid options depend on the transformer id", - "type": "object" + "description": "Options to be passed to the transformer\nValid options depend on the transformer id", + "type": "object", + "additionalProperties": {} }, "topic": { - "description": "Where to pull DataFrames from as input to transformation", - "type": "string" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataTopic" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDatasourceVariableKind": { "description": "Datasource variable kind", @@ -2042,18 +1980,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDatasourceVariableSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDatasourceVariableSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDatasourceVariableSpec": { "description": "Datasource variable specification", @@ -2077,22 +2010,16 @@ }, "allowCustomValue": { "type": "boolean", - "default": false + "default": true }, "current": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" }, "description": { "type": "string" }, "hide": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableHide" }, "includeAll": { "type": "boolean", @@ -2112,12 +2039,7 @@ "options": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" } }, "pluginId": { @@ -2125,8 +2047,7 @@ "default": "" }, "refresh": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableRefresh" }, "regex": { "type": "string", @@ -2136,7 +2057,8 @@ "type": "boolean", "default": false } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDynamicConfigValue": { "type": "object", @@ -2149,9 +2071,22 @@ "default": "" }, "value": { - "type": "object" + "type": "object", + "additionalProperties": {} } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardElement": { + "description": "Supported dashboard elements\n|* more element types in the future", + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardLibraryPanelKind" + } + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardElementReference": { "type": "object", @@ -2161,14 +2096,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "name": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFetchOptions": { "type": "object", @@ -2185,31 +2119,28 @@ "items": { "type": "array", "items": { - "type": "string", - "default": "" + "type": "string" } } }, "method": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardHttpRequestMethod" }, "queryParams": { - "description": "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array", + "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", "type": "array", "items": { "type": "array", "items": { - "type": "string", - "default": "" + "type": "string" } } }, "url": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldColor": { "description": "Map a field to a color.", @@ -2223,51 +2154,74 @@ "type": "string" }, "mode": { - "description": "The main color scheme mode.", - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldColorModeId" }, "seriesBy": { - "description": "Some visualizations need to know how to assign a series color from by value color schemes.", - "type": "string" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldColorSeriesByMode" } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldColorModeId": { + "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.", + "type": "string", + "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" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldColorSeriesByMode": { + "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.", + "type": "string", + "enum": [ + "min", + "max", + "last" + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldConfig": { - "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. 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.", + "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.", "type": "object", "properties": { "actions": { "description": "Define interactive HTTP requests that can be triggered from data visualizations.", "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAction" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAction" } }, "color": { - "description": "Panel color configuration", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldColor" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldColor" }, "custom": { - "description": "custom is specified by the FieldConfig field in panel plugin schemas.", + "description": "custom is specified by the FieldConfig field\nin panel plugin schemas.", "type": "object", "additionalProperties": { - "type": "object" + "type": "object", + "additionalProperties": {} } }, "decimals": { - "description": "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`.", - "type": "number", - "format": "double" + "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", @@ -2278,7 +2232,7 @@ "type": "string" }, "displayNameFromDS": { - "description": "This can be used by data sources that return and explicit naming structure for values and labels When this property is configured, this value is used rather than the default naming strategy.", + "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": { @@ -2289,54 +2243,49 @@ "description": "The behavior when clicking on a result", "type": "array", "items": { - "type": "object" + "type": "object", + "additionalProperties": {} } }, "mappings": { "description": "Convert input values into a display string", "type": "array", "items": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMapping" } }, "max": { "description": "The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.", - "type": "number", - "format": "double" + "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", - "format": "double" + "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, This will default to `${frame.meta.path}/${field.name}\n\nWhen defined, this value can be used as an identifier within the datasource scope, and may be used to update the results", + "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": { - "description": "Map numeric values to states", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardThresholdsConfig" - } - ] + "$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. You can use the units ID availables 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 unit, you can use the following formats: `suffix:\u003csuffix\u003e` for custom unit that should go after value. `prefix:\u003cprefix\u003e` for custom unit that should go before value. `time:\u003cformat\u003e` For custom date time formats type for example `time:YYYY-MM-DD`. `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. `count:\u003cunit\u003e` for a custom count unit. `currency:\u003cunit\u003e` 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:` 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.", "type": "string" }, "writeable": { "description": "True if data source can write a value to the path. Auth/authz are supported separately", "type": "boolean" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldConfigSource": { - "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. 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.", + "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.", "type": "object", "required": [ "defaults", @@ -2344,27 +2293,41 @@ ], "properties": { "defaults": { - "description": "Defaults are the options applied to all fields.", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldConfig" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldConfig" }, "overrides": { "description": "Overrides are the options applied to specific fields overriding the defaults.", "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1FieldConfigSourceOverrides" + "type": "object", + "required": [ + "matcher", + "properties" + ], + "properties": { + "__systemRef": { + "description": "Describes config override rules created when interacting with Grafana.", + "type": "string" + }, + "matcher": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMatcherConfig" + }, + "properties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDynamicConfigValue" + } } - ] + }, + "additionalProperties": false } } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFilterOrigin": { + "description": "Determine the origin of the adhoc variable filter", + "type": "string" }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutItemKind": { "type": "object", @@ -2374,18 +2337,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutItemSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutItemSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutItemSpec": { "type": "object", @@ -2398,38 +2356,25 @@ ], "properties": { "element": { - "description": "reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardElementReference" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardElementReference" }, "height": { - "type": "integer", - "format": "int64", - "default": 0 + "type": "integer" }, "repeat": { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRepeatOptions" }, "width": { - "type": "integer", - "format": "int64", - "default": 0 + "type": "integer" }, "x": { - "type": "integer", - "format": "int64", - "default": 0 + "type": "integer" }, "y": { - "type": "integer", - "format": "int64", - "default": 0 + "type": "integer" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutKind": { "type": "object", @@ -2439,52 +2384,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutSpec" } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind": { - "type": "object", - "properties": { - "AutoGridLayoutKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutKind" - }, - "GridLayoutKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutKind" - }, - "RowsLayoutKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutKind" - }, - "TabsLayoutKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutKind" - } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind": { - "type": "object", - "properties": { - "AutoGridLayoutKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutKind" - }, - "GridLayoutKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutKind" - }, - "RowsLayoutKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutKind" - }, - "TabsLayoutKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutKind" - } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutSpec": { "type": "object", @@ -2495,15 +2401,11 @@ "items": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutItemKind" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutItemKind" } } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGroupByVariableKind": { "description": "Group variable kind", @@ -2514,18 +2416,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGroupByVariableSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGroupByVariableSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGroupByVariableSpec": { "description": "GroupBy variable specification", @@ -2540,12 +2437,7 @@ ], "properties": { "current": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" }, "datasource": { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataSourceRef" @@ -2557,8 +2449,7 @@ "type": "string" }, "hide": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableHide" }, "label": { "type": "string" @@ -2574,19 +2465,25 @@ "options": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" } }, "skipUrlSync": { "type": "boolean", "default": false } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardHttpRequestMethod": { + "type": "string", + "enum": [ + "GET", + "PUT", + "POST", + "DELETE", + "PATCH" + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardInfinityOptions": { "type": "object", @@ -2600,39 +2497,35 @@ "type": "string" }, "datasourceUid": { - "type": "string", - "default": "" + "type": "string" }, "headers": { "type": "array", "items": { "type": "array", "items": { - "type": "string", - "default": "" + "type": "string" } } }, "method": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardHttpRequestMethod" }, "queryParams": { - "description": "These are 2D arrays of strings, each representing a key-value pair We are defining them this way because we can't generate a go struct that that would have exactly two strings in each sub-array", + "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", "type": "array", "items": { "type": "array", "items": { - "type": "string", - "default": "" + "type": "string" } } }, "url": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardIntervalVariableKind": { "description": "Interval variable kind", @@ -2643,18 +2536,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardIntervalVariableSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardIntervalVariableSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardIntervalVariableSpec": { "description": "Interval variable specification", @@ -2678,7 +2566,6 @@ }, "auto_count": { "type": "integer", - "format": "int64", "default": 0 }, "auto_min": { @@ -2686,19 +2573,13 @@ "default": "" }, "current": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" }, "description": { "type": "string" }, "hide": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableHide" }, "label": { "type": "string" @@ -2710,12 +2591,7 @@ "options": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" } }, "query": { @@ -2723,14 +2599,14 @@ "default": "" }, "refresh": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableRefresh" }, "skipUrlSync": { "type": "boolean", "default": false } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardLibraryPanelKind": { "type": "object", @@ -2740,18 +2616,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardLibraryPanelKindSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardLibraryPanelKindSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardLibraryPanelKindSpec": { "type": "object", @@ -2763,27 +2634,20 @@ "properties": { "id": { "description": "Panel ID for the library panel in the dashboard", - "type": "number", - "format": "double", - "default": 0 + "type": "number" }, "libraryPanel": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardLibraryPanelRef" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardLibraryPanelRef" }, "title": { "description": "Title for the library panel in the dashboard", - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardLibraryPanelRef": { - "description": "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.", + "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.", "type": "object", "required": [ "name", @@ -2792,15 +2656,14 @@ "properties": { "name": { "description": "Library panel name", - "type": "string", - "default": "" + "type": "string" }, "uid": { "description": "Library panel uid", - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardList": { "type": "object", @@ -2845,8 +2708,18 @@ } ] }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMappingType": { + "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.", + "type": "string", + "enum": [ + "value", + "range", + "regex", + "special" + ] + }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMatcherConfig": { - "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. It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type.", + "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.", "type": "object", "required": [ "id" @@ -2859,9 +2732,11 @@ }, "options": { "description": "The matcher options. This is specific to the matcher implementation.", - "type": "object" + "type": "object", + "additionalProperties": {} } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMetricFindValue": { "description": "Define the MetricFindValue type", @@ -2877,13 +2752,20 @@ "type": "string" }, "text": { - "type": "string", - "default": "" + "type": "string" }, "value": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardStringOrFloat64" + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelKind": { "type": "object", @@ -2893,29 +2775,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelSpec" } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelKindOrLibraryPanelKind": { - "type": "object", - "properties": { - "LibraryPanelKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardLibraryPanelKind" - }, - "PanelKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelKind" - } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelQueryKind": { "type": "object", @@ -2925,18 +2791,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelQuerySpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelQuerySpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelQuerySpec": { "type": "object", @@ -2950,22 +2811,16 @@ "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataSourceRef" }, "hidden": { - "type": "boolean", - "default": false + "type": "boolean" }, "query": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataQueryKind" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataQueryKind" }, "refId": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelSpec": { "type": "object", @@ -2979,49 +2834,31 @@ ], "properties": { "data": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryGroupKind" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryGroupKind" }, "description": { - "type": "string", - "default": "" + "type": "string" }, "id": { - "type": "number", - "format": "double", - "default": 0 + "type": "number" }, "links": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataLink" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataLink" } }, "title": { - "type": "string", - "default": "" + "type": "string" }, "transparent": { "type": "boolean" }, "vizConfig": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVizConfigKind" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVizConfigKind" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryGroupKind": { "type": "object", @@ -3031,18 +2868,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryGroupSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryGroupSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryGroupSpec": { "type": "object", @@ -3055,34 +2887,20 @@ "queries": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelQueryKind" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelQueryKind" } }, "queryOptions": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryOptionsSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryOptionsSpec" }, "transformations": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTransformationKind" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTransformationKind" } } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryOptionsSpec": { "type": "object", @@ -3097,12 +2915,10 @@ "type": "string" }, "maxDataPoints": { - "type": "integer", - "format": "int64" + "type": "integer" }, "queryCachingTTL": { - "type": "integer", - "format": "int64" + "type": "integer" }, "timeFrom": { "type": "string" @@ -3110,7 +2926,8 @@ "timeShift": { "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryVariableKind": { "description": "Query variable kind", @@ -3121,50 +2938,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryVariableSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryVariableSpec" } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind": { - "type": "object", - "properties": { - "AdhocVariableKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdhocVariableKind" - }, - "ConstantVariableKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConstantVariableKind" - }, - "CustomVariableKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardCustomVariableKind" - }, - "DatasourceVariableKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDatasourceVariableKind" - }, - "GroupByVariableKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGroupByVariableKind" - }, - "IntervalVariableKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardIntervalVariableKind" - }, - "QueryVariableKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryVariableKind" - }, - "SwitchVariableKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSwitchVariableKind" - }, - "TextVariableKind": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTextVariableKind" - } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryVariableSpec": { "description": "Query variable specification", @@ -3189,15 +2969,10 @@ }, "allowCustomValue": { "type": "boolean", - "default": false + "default": true }, "current": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" }, "datasource": { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataSourceRef" @@ -3209,8 +2984,7 @@ "type": "string" }, "hide": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableHide" }, "includeAll": { "type": "boolean", @@ -3230,28 +3004,17 @@ "options": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" } }, "placeholder": { "type": "string" }, "query": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataQueryKind" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataQueryKind" }, "refresh": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableRefresh" }, "regex": { "type": "string", @@ -3262,27 +3025,27 @@ "default": false }, "sort": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableSort" }, "staticOptions": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" } }, "staticOptionsOrder": { - "type": "string" + "type": "string", + "enum": [ + "before", + "after", + "sorted" + ] } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRangeMap": { - "description": "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.", + "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.", "type": "object", "required": [ "type", @@ -3291,21 +3054,35 @@ "properties": { "options": { "description": "Range to match against and the result to apply when the value is within the range", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1RangeMapOptions" + "type": "object", + "required": [ + "from", + "to", + "result" + ], + "properties": { + "from": { + "description": "Min value of the range. It can be null which means -Infinity", + "type": "number" + }, + "result": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMappingResult" + }, + "to": { + "description": "Max value of the range. It can be null which means +Infinity", + "type": "number" } - ] + }, + "additionalProperties": false }, "type": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMappingType" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRegexMap": { - "description": "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.", + "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.", "type": "object", "required": [ "type", @@ -3314,18 +3091,31 @@ "properties": { "options": { "description": "Regular expression to match against and the result to apply when the value matches the regex", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1RegexMapOptions" + "type": "object", + "required": [ + "pattern", + "result" + ], + "properties": { + "pattern": { + "description": "Regular expression to match against", + "type": "string" + }, + "result": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMappingResult" } - ] + }, + "additionalProperties": false }, "type": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMappingType" } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRepeatMode": { + "description": "other repeat modes will be added in the future: label, frame", + "type": "string" }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRepeatOptions": { "type": "object", @@ -3335,21 +3125,23 @@ ], "properties": { "direction": { - "type": "string" + "type": "string", + "enum": [ + "h", + "v" + ] }, "maxPerRow": { - "type": "integer", - "format": "int64" + "type": "integer" }, "mode": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRepeatMode" }, "value": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowRepeatOptions": { "type": "object", @@ -3359,14 +3151,13 @@ ], "properties": { "mode": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRepeatMode" }, "value": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutKind": { "type": "object", @@ -3376,18 +3167,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutRowKind": { "type": "object", @@ -3397,18 +3183,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutRowSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutRowSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutRowSpec": { "type": "object", @@ -3429,7 +3210,20 @@ "type": "boolean" }, "layout": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind" + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutKind" + } + ] }, "repeat": { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowRepeatOptions" @@ -3437,7 +3231,8 @@ "title": { "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutSpec": { "type": "object", @@ -3448,15 +3243,11 @@ "rows": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutRowKind" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutRowKind" } } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSpec": { "type": "object", @@ -3476,18 +3267,11 @@ "annotations": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationQueryKind" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAnnotationQueryKind" } }, "cursorSync": { - "description": "Configuration of dashboard cursor sync behavior. \"Off\" for no shared crosshair or tooltip (default). \"Crosshair\" for shared crosshair. \"Tooltip\" for shared crosshair AND shared tooltip.", - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDashboardCursorSync" }, "description": { "description": "Description of dashboard.", @@ -3495,31 +3279,40 @@ }, "editable": { "description": "Whether a dashboard is editable or not.", - "type": "boolean" + "type": "boolean", + "default": true }, "elements": { "type": "object", "additionalProperties": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardPanelKindOrLibraryPanelKind" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardElement" } }, "layout": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind" + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutKind" + } + ] }, "links": { "description": "Links with references to other dashboards or external websites.", "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDashboardLink" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDashboardLink" } }, "liveNow": { - "description": "When set to true, the dashboard will redraw panels at an interval matching the pixel width. This will keep data \"moving left\" regardless of the query refresh rate. This setting helps avoid dashboards presenting stale live data.", + "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": { @@ -3528,42 +3321,35 @@ "default": false }, "revision": { - "description": "Plugins only. The version of the dashboard installed together with the plugin. This is used to determine if the dashboard should be updated when the plugin is updated.", - "type": "integer", - "format": "int32" + "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.", "type": "array", "items": { - "type": "string", - "default": "" + "type": "string" } }, "timeSettings": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTimeSettingsSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTimeSettingsSpec" }, "title": { "description": "Title of dashboard.", - "type": "string", - "default": "" + "type": "string" }, "variables": { "description": "Configured template variables.", "type": "array", "items": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableKind" } } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSpecialValueMap": { - "description": "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.", + "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.", "type": "object", "required": [ "type", @@ -3571,58 +3357,47 @@ ], "properties": { "options": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1SpecialValueMapOptions" + "type": "object", + "required": [ + "match", + "result" + ], + "properties": { + "match": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSpecialValueMatch" + }, + "result": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMappingResult" } - ] + }, + "additionalProperties": false }, "type": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMappingType" } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSpecialValueMatch": { + "description": "Special value types supported by the `SpecialValueMap`", + "type": "string", + "enum": [ + "true", + "false", + "null", + "nan", + "null+nan", + "empty" + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardStatus": { "type": "object", "properties": { "conversion": { - "description": "Optional conversion status.", - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConversionStatus" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConversionStatus" } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardStringOrArrayOfString": { - "type": "object", - "properties": { - "ArrayOfString": { - "type": "array", - "items": { - "type": "string", - "default": "" - } - }, - "String": { - "type": "string" - } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardStringOrFloat64": { - "type": "object", - "properties": { - "Float64": { - "type": "number", - "format": "double" - }, - "String": { - "type": "string" - } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSwitchVariableKind": { "type": "object", @@ -3632,18 +3407,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSwitchVariableSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSwitchVariableSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSwitchVariableSpec": { "description": "Switch variable specification", @@ -3659,22 +3429,21 @@ "properties": { "current": { "type": "string", - "default": "" + "default": "false" }, "description": { "type": "string" }, "disabledValue": { "type": "string", - "default": "" + "default": "false" }, "enabledValue": { "type": "string", - "default": "" + "default": "true" }, "hide": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableHide" }, "label": { "type": "string" @@ -3687,7 +3456,8 @@ "type": "boolean", "default": false } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabRepeatOptions": { "type": "object", @@ -3697,14 +3467,13 @@ ], "properties": { "mode": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRepeatMode" }, "value": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutKind": { "type": "object", @@ -3714,18 +3483,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutSpec": { "type": "object", @@ -3736,15 +3500,11 @@ "tabs": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutTabKind" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutTabKind" } } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutTabKind": { "type": "object", @@ -3754,18 +3514,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutTabSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutTabSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutTabSpec": { "type": "object", @@ -3777,7 +3532,20 @@ "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConditionalRenderingGroupKind" }, "layout": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind" + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRowsLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAutoGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabsLayoutKind" + } + ] }, "repeat": { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTabRepeatOptions" @@ -3785,7 +3553,8 @@ "title": { "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTextVariableKind": { "description": "Text variable kind", @@ -3796,18 +3565,13 @@ ], "properties": { "kind": { - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTextVariableSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTextVariableSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTextVariableSpec": { "description": "Text variable specification", @@ -3821,19 +3585,13 @@ ], "properties": { "current": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption" }, "description": { "type": "string" }, "hide": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableHide" }, "label": { "type": "string" @@ -3850,7 +3608,8 @@ "type": "boolean", "default": false } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardThreshold": { "type": "object", @@ -3860,15 +3619,14 @@ ], "properties": { "color": { - "type": "string", - "default": "" + "type": "string" }, "value": { "description": "Value null means -Infinity", - "type": "number", - "format": "double" + "type": "number" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardThresholdsConfig": { "type": "object", @@ -3878,21 +3636,23 @@ ], "properties": { "mode": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardThresholdsMode" }, "steps": { "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardThreshold" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardThreshold" } } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardThresholdsMode": { + "type": "string", + "enum": [ + "absolute", + "percentage" + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTimeRangeOption": { "type": "object", @@ -3904,20 +3664,21 @@ "properties": { "display": { "type": "string", - "default": "" + "default": "Last 6 hours" }, "from": { "type": "string", - "default": "" + "default": "now-6h" }, "to": { "type": "string", - "default": "" + "default": "now" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTimeSettingsSpec": { - "description": "Time configuration It defines the default time config for the time picker, the refresh picker for the specific dashboard.", + "description": "Time configuration\nIt defines the default time config for the time picker, the refresh picker for the specific dashboard.", "type": "object", "required": [ "from", @@ -3929,64 +3690,76 @@ ], "properties": { "autoRefresh": { - "description": "Refresh rate of dashboard. Represented via interval string, e.g. \"5s\", \"1m\", \"1h\", \"1d\". v1: refresh", + "description": "Refresh rate of dashboard. Represented via interval string, e.g. \"5s\", \"1m\", \"1h\", \"1d\".\nv1: refresh", "type": "string", "default": "" }, "autoRefreshIntervals": { - "description": "Interval options available in the refresh picker dropdown. v1: timepicker.refresh_intervals", + "description": "Interval options available in the refresh picker dropdown.\nv1: timepicker.refresh_intervals", "type": "array", + "default": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], "items": { - "type": "string", - "default": "" + "type": "string" } }, "fiscalYearStartMonth": { "description": "The month that the fiscal year starts on. 0 = January, 11 = December", "type": "integer", - "format": "int64", "default": 0 }, "from": { - "description": "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\".", + "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", - "default": "" + "default": "now-6h" }, "hideTimepicker": { - "description": "Whether timepicker is visible or not. v1: timepicker.hidden", + "description": "Whether timepicker is visible or not.\nv1: timepicker.hidden", "type": "boolean", "default": false }, "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. v1: timepicker.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. v1: timepicker.quick_ranges , not exposed in the UI", + "description": "Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard.\nv1: timepicker.quick_ranges , not exposed in the UI", "type": "array", "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTimeRangeOption" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTimeRangeOption" } }, "timezone": { "description": "Timezone of dashboard. Accepted values are IANA TZDB zone ID or \"browser\" or \"utc\".", - "type": "string" + "type": "string", + "default": "browser" }, "to": { - "description": "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\".", + "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", - "default": "" + "default": "now" }, "weekStart": { "description": "Day when the week starts. Expressed by the name of the day in lowercase, e.g. \"monday\".", - "type": "string" + "type": "string", + "enum": [ + "saturday", + "monday", + "sunday" + ] } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTransformationKind": { "type": "object", @@ -3997,136 +3770,16 @@ "properties": { "kind": { "description": "The kind of a TransformationKind is the transformation ID", - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataTransformerConfig" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDataTransformerConfig" } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1ActionStyle": { - "type": "object", - "properties": { - "backgroundColor": { - "type": "string" - } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1FieldConfigSourceOverrides": { - "type": "object", - "required": [ - "matcher", - "properties" - ], - "properties": { - "__systemRef": { - "description": "Describes config override rules created when interacting with Grafana.", - "type": "string" - }, - "matcher": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMatcherConfig" - } - ] - }, - "properties": { - "type": "array", - "items": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDynamicConfigValue" - } - ] - } - } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1RangeMapOptions": { - "type": "object", - "required": [ - "from", - "to", - "result" - ], - "properties": { - "from": { - "description": "Min value of the range. It can be null which means -Infinity", - "type": "number", - "format": "double" - }, - "result": { - "description": "Config to apply when the value is within the range", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMappingResult" - } - ] - }, - "to": { - "description": "Max value of the range. It can be null which means +Infinity", - "type": "number", - "format": "double" - } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1RegexMapOptions": { - "type": "object", - "required": [ - "pattern", - "result" - ], - "properties": { - "pattern": { - "description": "Regular expression to match against", - "type": "string", - "default": "" - }, - "result": { - "description": "Config to apply when the value matches the regex", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMappingResult" - } - ] - } - } - }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardV2alpha1SpecialValueMapOptions": { - "type": "object", - "required": [ - "match", - "result" - ], - "properties": { - "match": { - "description": "Special value to match against", - "type": "string", - "default": "" - }, - "result": { - "description": "Config to apply when the value matches the special value", - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMappingResult" - } - ] - } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMap": { - "description": "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.", + "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.", "type": "object", "required": [ "type", @@ -4134,39 +3787,33 @@ ], "properties": { "options": { - "description": "Map with \u003cvalue_to_match\u003e: ValueMappingResult. For example: { \"10\": { text: \"Perfection!\", color: \"green\" } }", + "description": "Map with : ValueMappingResult. For example: { \"10\": { text: \"Perfection!\", color: \"green\" } }", "type": "object", "additionalProperties": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMappingResult" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMappingResult" } }, "type": { - "type": "string", - "default": "" + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardMappingType" } - } + }, + "additionalProperties": false }, - "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap": { - "type": "object", - "properties": { - "RangeMap": { + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMapping": { + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMap" + }, + { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRangeMap" }, - "RegexMap": { + { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardRegexMap" }, - "SpecialValueMap": { + { "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSpecialValueMap" - }, - "ValueMap": { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMap" } - } + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardValueMappingResult": { "description": "Result used as replacement with text and color when the value matches", @@ -4182,14 +3829,54 @@ }, "index": { "description": "Position in the mapping array. Only used internally.", - "type": "integer", - "format": "int32" + "type": "integer" }, "text": { "description": "Text to display when the value matches", "type": "string" } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableHide": { + "description": "Determine if the variable shows on dashboard\nAccepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing).", + "type": "string", + "enum": [ + "dontHide", + "hideLabel", + "hideVariable" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableKind": { + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardQueryVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardTextVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardConstantVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardDatasourceVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardIntervalVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardCustomVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardGroupByVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardAdhocVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardSwitchVariableKind" + } + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableOption": { "description": "Variable option specification", @@ -4205,21 +3892,58 @@ }, "text": { "description": "Text to be displayed for the option", - "allOf": [ + "oneOf": [ { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardStringOrArrayOfString" + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } } ] }, "value": { "description": "Value of the option", - "allOf": [ + "oneOf": [ { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardStringOrArrayOfString" + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } } ] } - } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableRefresh": { + "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.", + "type": "string", + "enum": [ + "never", + "onDashboardLoad", + "onTimeRangeChanged" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVariableSort": { + "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", + "type": "string", + "enum": [ + "disabled", + "alphabeticalAsc", + "alphabeticalDesc", + "numericalAsc", + "numericalDesc", + "alphabeticalCaseInsensitiveAsc", + "alphabeticalCaseInsensitiveDesc", + "naturalAsc", + "naturalDesc" + ] }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVizConfigKind": { "type": "object", @@ -4230,18 +3954,13 @@ "properties": { "kind": { "description": "The kind of a VizConfigKind is the plugin ID", - "type": "string", - "default": "" + "type": "string" }, "spec": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVizConfigSpec" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVizConfigSpec" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardVizConfigSpec": { "description": "--- Kinds ---", @@ -4253,24 +3972,20 @@ ], "properties": { "fieldConfig": { - "default": {}, - "allOf": [ - { - "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldConfigSource" - } - ] + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardFieldConfigSource" }, "options": { "type": "object", "additionalProperties": { - "type": "object" + "type": "object", + "additionalProperties": {} } }, "pluginVersion": { - "type": "string", - "default": "" + "type": "string" } - } + }, + "additionalProperties": false }, "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2alpha1.DashboardWithAccessInfo": { "description": "This is like the legacy DTO where access and metadata are all returned in a single call", @@ -4488,7 +4203,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:\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", + "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", "type": "object" }, "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": { @@ -4842,4 +4557,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 new file mode 100644 index 00000000000..198ad3aea25 --- /dev/null +++ b/pkg/tests/apis/openapi_snapshots/dashboard.grafana.app-v2beta1.json @@ -0,0 +1,4591 @@ +{ + "openapi": "3.0.0", + "info": { + "description": "Grafana dashboards as resources", + "title": "dashboard.grafana.app/v2beta1" + }, + "paths": { + "/apis/dashboard.grafana.app/v2beta1/": { + "get": { + "tags": [ + "API Discovery" + ], + "description": "Describe the available kubernetes resources", + "operationId": "getAPIResources", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList" + } + } + } + } + } + } + }, + "/apis/dashboard.grafana.app/v2beta1/namespaces/{namespace}/dashboards": { + "get": { + "tags": [ + "Dashboard" + ], + "description": "list objects of kind Dashboard", + "operationId": "listDashboard", + "parameters": [ + { + "name": "allowWatchBookmarks", + "in": "query", + "description": "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.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "continue", + "in": "query", + "description": "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\".\n\nThis 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.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "labelSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "limit", + "in": "query", + "description": "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.\n\nThe 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.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "resourceVersion", + "in": "query", + "description": "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.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "resourceVersionMatch", + "in": "query", + "description": "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.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "sendInitialEvents", + "in": "query", + "description": "`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.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "timeoutSeconds", + "in": "query", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "watch", + "in": "query", + "description": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardList" + } + }, + "application/json;stream=watch": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardList" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardList" + } + }, + "application/vnd.kubernetes.protobuf;stream=watch": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardList" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardList" + } + } + } + } + }, + "x-kubernetes-action": "list", + "x-kubernetes-group-version-kind": { + "group": "dashboard.grafana.app", + "version": "v2beta1", + "kind": "Dashboard" + } + }, + "post": { + "tags": [ + "Dashboard" + ], + "description": "create a Dashboard", + "operationId": "createDashboard", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "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", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldManager", + "in": "query", + "description": "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.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldValidation", + "in": "query", + "description": "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.", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + } + } + } + }, + "x-kubernetes-action": "post", + "x-kubernetes-group-version-kind": { + "group": "dashboard.grafana.app", + "version": "v2beta1", + "kind": "Dashboard" + } + }, + "delete": { + "tags": [ + "Dashboard" + ], + "description": "delete collection of Dashboard", + "operationId": "deletecollectionDashboard", + "parameters": [ + { + "name": "continue", + "in": "query", + "description": "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\".\n\nThis 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.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "dryRun", + "in": "query", + "description": "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", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "gracePeriodSeconds", + "in": "query", + "description": "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.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "in": "query", + "description": "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", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "labelSelector", + "in": "query", + "description": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "limit", + "in": "query", + "description": "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.\n\nThe 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.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "orphanDependents", + "in": "query", + "description": "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.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "propagationPolicy", + "in": "query", + "description": "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.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "resourceVersion", + "in": "query", + "description": "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.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "resourceVersionMatch", + "in": "query", + "description": "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.\n\nDefaults to unset", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "sendInitialEvents", + "in": "query", + "description": "`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.\n\nWhen `sendInitialEvents` option is set, we require `resourceVersionMatch` option to also be set. The semantic of the watch request is as following: - `resourceVersionMatch` = NotOlderThan\n is interpreted as \"data at least as new as the provided `resourceVersion`\"\n and the bookmark event is send when the state is synced\n to a `resourceVersion` at least as fresh as the one provided by the ListOptions.\n If `resourceVersion` is unset, this is interpreted as \"consistent read\" and the\n bookmark event is send when the state is synced at least to the moment\n when request started being processed.\n- `resourceVersionMatch` set to any other value or unset\n Invalid error is returned.\n\nDefaults to true if `resourceVersion=\"\"` or `resourceVersion=\"0\"` (for backward compatibility reasons) and to false otherwise.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "timeoutSeconds", + "in": "query", + "description": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", + "schema": { + "type": "integer", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + } + } + }, + "x-kubernetes-action": "deletecollection", + "x-kubernetes-group-version-kind": { + "group": "dashboard.grafana.app", + "version": "v2beta1", + "kind": "Dashboard" + } + }, + "parameters": [ + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "pretty", + "in": "query", + "description": "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).", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/dashboard.grafana.app/v2beta1/namespaces/{namespace}/dashboards/{name}": { + "get": { + "tags": [ + "Dashboard" + ], + "description": "read the specified Dashboard", + "operationId": "getDashboard", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + } + } + } + }, + "x-kubernetes-action": "get", + "x-kubernetes-group-version-kind": { + "group": "dashboard.grafana.app", + "version": "v2beta1", + "kind": "Dashboard" + } + }, + "put": { + "tags": [ + "Dashboard" + ], + "description": "replace the specified Dashboard", + "operationId": "replaceDashboard", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "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", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldManager", + "in": "query", + "description": "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.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldValidation", + "in": "query", + "description": "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.", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + } + } + } + }, + "x-kubernetes-action": "put", + "x-kubernetes-group-version-kind": { + "group": "dashboard.grafana.app", + "version": "v2beta1", + "kind": "Dashboard" + } + }, + "delete": { + "tags": [ + "Dashboard" + ], + "description": "delete a Dashboard", + "operationId": "deleteDashboard", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "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", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "gracePeriodSeconds", + "in": "query", + "description": "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.", + "schema": { + "type": "integer", + "uniqueItems": true + } + }, + { + "name": "ignoreStoreReadErrorWithClusterBreakingPotential", + "in": "query", + "description": "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", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "orphanDependents", + "in": "query", + "description": "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.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + }, + { + "name": "propagationPolicy", + "in": "query", + "description": "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.", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + } + }, + "202": { + "description": "Accepted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Status" + } + } + } + } + }, + "x-kubernetes-action": "delete", + "x-kubernetes-group-version-kind": { + "group": "dashboard.grafana.app", + "version": "v2beta1", + "kind": "Dashboard" + } + }, + "patch": { + "tags": [ + "Dashboard" + ], + "description": "partially update the specified Dashboard", + "operationId": "updateDashboard", + "parameters": [ + { + "name": "dryRun", + "in": "query", + "description": "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", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldManager", + "in": "query", + "description": "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).", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "fieldValidation", + "in": "query", + "description": "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.", + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "force", + "in": "query", + "description": "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.", + "schema": { + "type": "boolean", + "uniqueItems": true + } + } + ], + "requestBody": { + "content": { + "application/apply-patch+yaml": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + }, + "application/strategic-merge-patch+json": { + "schema": { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/vnd.kubernetes.protobuf": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + }, + "application/yaml": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + } + } + } + }, + "x-kubernetes-action": "patch", + "x-kubernetes-group-version-kind": { + "group": "dashboard.grafana.app", + "version": "v2beta1", + "kind": "Dashboard" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the Dashboard", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "pretty", + "in": "query", + "description": "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).", + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + }, + "/apis/dashboard.grafana.app/v2beta1/namespaces/{namespace}/dashboards/{name}/dto": { + "get": { + "tags": [ + "Dashboard" + ], + "description": "connect GET requests to dto of Dashboard", + "operationId": "getDashboardDto", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardWithAccessInfo" + } + } + } + } + }, + "x-kubernetes-action": "connect", + "x-kubernetes-group-version-kind": { + "group": "dashboard.grafana.app", + "version": "v2beta1", + "kind": "DashboardWithAccessInfo" + } + }, + "parameters": [ + { + "name": "name", + "in": "path", + "description": "name of the DashboardWithAccessInfo", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + }, + { + "name": "namespace", + "in": "path", + "description": "object name and auth scope, such as for teams and projects", + "required": true, + "schema": { + "type": "string", + "uniqueItems": true + } + } + ] + } + }, + "components": { + "schemas": { + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.AnnotationActions": { + "type": "object", + "required": [ + "canAdd", + "canEdit", + "canDelete" + ], + "properties": { + "canAdd": { + "type": "boolean", + "default": false + }, + "canDelete": { + "type": "boolean", + "default": false + }, + "canEdit": { + "type": "boolean", + "default": false + } + } + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.AnnotationPermission": { + "type": "object", + "required": [ + "dashboard", + "organization" + ], + "properties": { + "dashboard": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.AnnotationActions" + } + ] + }, + "organization": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.AnnotationActions" + } + ] + } + } + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard": { + "type": "object", + "required": [ + "kind", + "apiVersion", + "metadata", + "spec" + ], + "properties": { + "apiVersion": { + "description": "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", + "type": "string" + }, + "kind": { + "description": "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", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + ] + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardSpec" + }, + "status": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardStatus" + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "dashboard.grafana.app", + "kind": "Dashboard", + "version": "v2beta1" + } + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAccess": { + "description": "Information about how the requesting user can use a given dashboard", + "type": "object", + "required": [ + "isPublic", + "canSave", + "canEdit", + "canAdmin", + "canStar", + "canDelete", + "annotationsPermissions" + ], + "properties": { + "annotationsPermissions": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.AnnotationPermission" + }, + "canAdmin": { + "type": "boolean", + "default": false + }, + "canDelete": { + "type": "boolean", + "default": false + }, + "canEdit": { + "type": "boolean", + "default": false + }, + "canSave": { + "description": "The permissions part", + "type": "boolean", + "default": false + }, + "canStar": { + "type": "boolean", + "default": false + }, + "isPublic": { + "type": "boolean", + "default": false + }, + "slug": { + "description": "Metadata fields", + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAction": { + "type": "object", + "required": [ + "type", + "title" + ], + "properties": { + "confirmation": { + "type": "string" + }, + "fetch": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFetchOptions" + }, + "infinity": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardInfinityOptions" + }, + "oneClick": { + "type": "boolean" + }, + "style": { + "type": "object", + "properties": { + "backgroundColor": { + "type": "string" + } + }, + "additionalProperties": false + }, + "title": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardActionType" + }, + "variables": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardActionVariable" + } + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardActionType": { + "type": "string", + "enum": [ + "fetch", + "infinity" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardActionVariable": { + "type": "object", + "required": [ + "key", + "name", + "type" + ], + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardActionVariableType" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardActionVariableType": { + "description": "Action variable type", + "type": "string" + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAdHocFilterWithLabels": { + "description": "Define the AdHocFilterWithLabels type", + "type": "object", + "required": [ + "key", + "operator", + "value" + ], + "properties": { + "condition": { + "description": "@deprecated", + "type": "string" + }, + "forceEdit": { + "type": "boolean" + }, + "key": { + "type": "string" + }, + "keyLabel": { + "type": "string" + }, + "operator": { + "type": "string" + }, + "origin": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFilterOrigin" + }, + "value": { + "type": "string" + }, + "valueLabels": { + "type": "array", + "items": { + "type": "string" + } + }, + "values": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAdhocVariableKind": { + "description": "Adhoc variable kind", + "type": "object", + "required": [ + "kind", + "group", + "spec" + ], + "properties": { + "datasource": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "group": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAdhocVariableSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAdhocVariableSpec": { + "description": "Adhoc variable specification", + "type": "object", + "required": [ + "name", + "baseFilters", + "filters", + "defaultKeys", + "hide", + "skipUrlSync", + "allowCustomValue" + ], + "properties": { + "allowCustomValue": { + "type": "boolean", + "default": true + }, + "baseFilters": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAdHocFilterWithLabels" + } + }, + "defaultKeys": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardMetricFindValue" + } + }, + "description": { + "type": "string" + }, + "filters": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAdHocFilterWithLabels" + } + }, + "hide": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableHide" + }, + "label": { + "type": "string" + }, + "name": { + "type": "string", + "default": "" + }, + "skipUrlSync": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAnnotationEventFieldMapping": { + "description": "Annotation event field mapping. Defines how to map a data frame field to an annotation event field.", + "type": "object", + "properties": { + "regex": { + "description": "Regular expression to apply to the field value", + "type": "string" + }, + "source": { + "description": "Source type for the field value", + "type": "string", + "default": "field" + }, + "value": { + "description": "Constant value to use when source is \"text\"", + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAnnotationPanelFilter": { + "type": "object", + "required": [ + "ids" + ], + "properties": { + "exclude": { + "description": "Should the specified panels be included or excluded", + "type": "boolean", + "default": false + }, + "ids": { + "description": "Panel IDs that should be included or excluded", + "type": "array", + "items": { + "type": "integer" + } + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAnnotationQueryKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAnnotationQuerySpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAnnotationQueryPlacement": { + "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" + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAnnotationQuerySpec": { + "type": "object", + "required": [ + "query", + "enable", + "hide", + "iconColor", + "name" + ], + "properties": { + "builtIn": { + "type": "boolean", + "default": false + }, + "enable": { + "type": "boolean" + }, + "filter": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAnnotationPanelFilter" + }, + "hide": { + "type": "boolean" + }, + "iconColor": { + "type": "string" + }, + "legacyOptions": { + "description": "Catch-all field for datasource-specific properties. Should not be available in as code tooling.", + "type": "object", + "additionalProperties": true + }, + "mappings": { + "description": "Mappings define how to convert data frame fields to annotation event fields.", + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAnnotationEventFieldMapping" + } + }, + "name": { + "type": "string" + }, + "placement": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAnnotationQueryPlacement" + }, + "query": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDataQueryKind" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridLayoutItemKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridLayoutItemSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridLayoutItemSpec": { + "type": "object", + "required": [ + "element" + ], + "properties": { + "conditionalRendering": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingGroupKind" + }, + "element": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardElementReference" + }, + "repeat": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridRepeatOptions" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridLayoutKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridLayoutSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridLayoutSpec": { + "type": "object", + "required": [ + "columnWidthMode", + "rowHeightMode", + "items" + ], + "properties": { + "columnWidth": { + "type": "number" + }, + "columnWidthMode": { + "type": "string", + "default": "standard", + "enum": [ + "narrow", + "standard", + "wide", + "custom" + ] + }, + "fillScreen": { + "type": "boolean", + "default": false + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridLayoutItemKind" + } + }, + "maxColumnCount": { + "type": "number", + "default": 3 + }, + "rowHeight": { + "type": "number" + }, + "rowHeightMode": { + "type": "string", + "default": "standard", + "enum": [ + "short", + "standard", + "tall", + "custom" + ] + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridRepeatOptions": { + "type": "object", + "required": [ + "mode", + "value" + ], + "properties": { + "mode": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRepeatMode" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingDataKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingDataSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingDataSpec": { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingGroupKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingGroupSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingGroupSpec": { + "type": "object", + "required": [ + "visibility", + "condition", + "items" + ], + "properties": { + "condition": { + "type": "string", + "enum": [ + "and", + "or" + ] + }, + "items": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingDataKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingTimeRangeSizeKind" + } + ] + } + }, + "visibility": { + "type": "string", + "enum": [ + "show", + "hide" + ] + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingTimeRangeSizeKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingTimeRangeSizeSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingTimeRangeSizeSpec": { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingVariableKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingVariableSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingVariableSpec": { + "type": "object", + "required": [ + "variable", + "operator", + "value" + ], + "properties": { + "operator": { + "type": "string", + "enum": [ + "equals", + "notEquals", + "matches", + "notMatches" + ] + }, + "value": { + "type": "string" + }, + "variable": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConstantVariableKind": { + "description": "Constant variable kind", + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConstantVariableSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConstantVariableSpec": { + "description": "Constant variable specification", + "type": "object", + "required": [ + "name", + "query", + "current", + "hide", + "skipUrlSync" + ], + "properties": { + "current": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + }, + "description": { + "type": "string" + }, + "hide": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableHide" + }, + "label": { + "type": "string" + }, + "name": { + "type": "string", + "default": "" + }, + "query": { + "type": "string", + "default": "" + }, + "skipUrlSync": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConversionStatus": { + "description": "ConversionStatus is the status of the conversion of the dashboard.", + "type": "object", + "required": [ + "failed" + ], + "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": { + "description": "The original value map[string]any", + "type": "object", + "additionalProperties": {} + }, + "storedVersion": { + "description": "The version which was stored when the dashboard was created / updated.\nFetching this version should always succeed.", + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardCustomVariableKind": { + "description": "Custom variable kind", + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardCustomVariableSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardCustomVariableSpec": { + "description": "Custom variable specification", + "type": "object", + "required": [ + "name", + "query", + "current", + "options", + "multi", + "includeAll", + "hide", + "skipUrlSync", + "allowCustomValue" + ], + "properties": { + "allValue": { + "type": "string" + }, + "allowCustomValue": { + "type": "boolean", + "default": true + }, + "current": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + }, + "description": { + "type": "string" + }, + "hide": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableHide" + }, + "includeAll": { + "type": "boolean", + "default": false + }, + "label": { + "type": "string" + }, + "multi": { + "type": "boolean", + "default": false + }, + "name": { + "type": "string", + "default": "" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + } + }, + "query": { + "type": "string", + "default": "" + }, + "skipUrlSync": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDashboardCursorSync": { + "description": "\"Off\" for no shared crosshair or tooltip (default).\n\"Crosshair\" for shared crosshair.\n\"Tooltip\" for shared crosshair AND shared tooltip.", + "type": "string", + "enum": [ + "Crosshair", + "Tooltip", + "Off" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDashboardLink": { + "description": "Links with references to other dashboards or external resources", + "type": "object", + "required": [ + "title", + "type", + "icon", + "tooltip", + "tags", + "asDropdown", + "targetBlank", + "includeVars", + "keepTime" + ], + "properties": { + "asDropdown": { + "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", + "default": false + }, + "icon": { + "description": "Icon name to be displayed with the link", + "type": "string" + }, + "includeVars": { + "description": "If true, includes current template variables values in the link as query params", + "type": "boolean", + "default": false + }, + "keepTime": { + "description": "If true, includes current time range in the link as query params", + "type": "boolean", + "default": false + }, + "placement": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDashboardLinkPlacement" + }, + "tags": { + "description": "List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards", + "type": "array", + "items": { + "type": "string" + } + }, + "targetBlank": { + "description": "If true, the link will be opened in a new tab", + "type": "boolean", + "default": false + }, + "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/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDashboardLinkType" + }, + "url": { + "description": "Link URL. Only required/valid if the type is link", + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDashboardLinkPlacement": { + "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" + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDashboardLinkType": { + "description": "Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)", + "type": "string", + "enum": [ + "link", + "dashboards" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDataLink": { + "type": "object", + "required": [ + "title", + "url" + ], + "properties": { + "targetBlank": { + "type": "boolean" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDataQueryKind": { + "type": "object", + "required": [ + "kind", + "group", + "version", + "spec" + ], + "properties": { + "datasource": { + "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.", + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "group": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "spec": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": {} + } + }, + "version": { + "type": "string", + "default": "v0" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDataTopic": { + "description": "A topic is attached to DataFrame metadata in query results.\nThis specifies where the data should be used.", + "type": "string", + "enum": [ + "series", + "annotations", + "alertStates" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDataTransformerConfig": { + "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.", + "type": "object", + "required": [ + "id", + "options" + ], + "properties": { + "disabled": { + "description": "Disabled transformations are skipped", + "type": "boolean" + }, + "filter": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardMatcherConfig" + }, + "id": { + "description": "Unique identifier of transformer", + "type": "string" + }, + "options": { + "description": "Options to be passed to the transformer\nValid options depend on the transformer id", + "type": "object", + "additionalProperties": {} + }, + "topic": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDataTopic" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDatasourceVariableKind": { + "description": "Datasource variable kind", + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDatasourceVariableSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDatasourceVariableSpec": { + "description": "Datasource variable specification", + "type": "object", + "required": [ + "name", + "pluginId", + "refresh", + "regex", + "current", + "options", + "multi", + "includeAll", + "hide", + "skipUrlSync", + "allowCustomValue" + ], + "properties": { + "allValue": { + "type": "string" + }, + "allowCustomValue": { + "type": "boolean", + "default": true + }, + "current": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + }, + "description": { + "type": "string" + }, + "hide": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableHide" + }, + "includeAll": { + "type": "boolean", + "default": false + }, + "label": { + "type": "string" + }, + "multi": { + "type": "boolean", + "default": false + }, + "name": { + "type": "string", + "default": "" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + } + }, + "pluginId": { + "type": "string", + "default": "" + }, + "refresh": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableRefresh" + }, + "regex": { + "type": "string", + "default": "" + }, + "skipUrlSync": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDynamicConfigValue": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string", + "default": "" + }, + "value": { + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardElement": { + "description": "Supported dashboard elements\n|* more element types in the future", + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardPanelKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardLibraryPanelKind" + } + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardElementReference": { + "type": "object", + "required": [ + "kind", + "name" + ], + "properties": { + "kind": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFetchOptions": { + "type": "object", + "required": [ + "method", + "url" + ], + "properties": { + "body": { + "type": "string" + }, + "headers": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "method": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardHttpRequestMethod" + }, + "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", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFieldColor": { + "description": "Map a field to a color.", + "type": "object", + "required": [ + "mode" + ], + "properties": { + "fixedColor": { + "description": "The fixed color value for fixed or shades color modes.", + "type": "string" + }, + "mode": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFieldColorModeId" + }, + "seriesBy": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFieldColorSeriesByMode" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFieldColorModeId": { + "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.", + "type": "string", + "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" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFieldColorSeriesByMode": { + "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.", + "type": "string", + "enum": [ + "min", + "max", + "last" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFieldConfig": { + "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.", + "type": "object", + "properties": { + "actions": { + "description": "Define interactive HTTP requests that can be triggered from data visualizations.", + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAction" + } + }, + "color": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFieldColor" + }, + "custom": { + "description": "custom is specified by the FieldConfig field\nin panel plugin schemas.", + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": {} + } + }, + "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", + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} + } + }, + "mappings": { + "description": "Convert input values into a display string", + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardValueMapping" + } + }, + "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/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.", + "type": "string" + }, + "writeable": { + "description": "True if data source can write a value to the path. Auth/authz are supported separately", + "type": "boolean" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFieldConfigSource": { + "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.", + "type": "object", + "required": [ + "defaults", + "overrides" + ], + "properties": { + "defaults": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFieldConfig" + }, + "overrides": { + "description": "Overrides are the options applied to specific fields overriding the defaults.", + "type": "array", + "items": { + "type": "object", + "required": [ + "matcher", + "properties" + ], + "properties": { + "__systemRef": { + "description": "Describes config override rules created when interacting with Grafana.", + "type": "string" + }, + "matcher": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardMatcherConfig" + }, + "properties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDynamicConfigValue" + } + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFilterOrigin": { + "description": "Determine the origin of the adhoc variable filter", + "type": "string" + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGridLayoutItemKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGridLayoutItemSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGridLayoutItemSpec": { + "type": "object", + "required": [ + "x", + "y", + "width", + "height", + "element" + ], + "properties": { + "element": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardElementReference" + }, + "height": { + "type": "integer" + }, + "repeat": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRepeatOptions" + }, + "width": { + "type": "integer" + }, + "x": { + "type": "integer" + }, + "y": { + "type": "integer" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGridLayoutKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGridLayoutSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGridLayoutSpec": { + "type": "object", + "required": [ + "items" + ], + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGridLayoutItemKind" + } + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGroupByVariableKind": { + "description": "Group variable kind", + "type": "object", + "required": [ + "kind", + "group", + "spec" + ], + "properties": { + "datasource": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "group": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGroupByVariableSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGroupByVariableSpec": { + "description": "GroupBy variable specification", + "type": "object", + "required": [ + "name", + "current", + "options", + "multi", + "hide", + "skipUrlSync" + ], + "properties": { + "current": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + }, + "defaultValue": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + }, + "description": { + "type": "string" + }, + "hide": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableHide" + }, + "label": { + "type": "string" + }, + "multi": { + "type": "boolean", + "default": false + }, + "name": { + "type": "string", + "default": "" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + } + }, + "skipUrlSync": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardHttpRequestMethod": { + "type": "string", + "enum": [ + "GET", + "PUT", + "POST", + "DELETE", + "PATCH" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardInfinityOptions": { + "type": "object", + "required": [ + "method", + "url", + "datasourceUid" + ], + "properties": { + "body": { + "type": "string" + }, + "datasourceUid": { + "type": "string" + }, + "headers": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "method": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardHttpRequestMethod" + }, + "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", + "type": "array", + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardIntervalVariableKind": { + "description": "Interval variable kind", + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardIntervalVariableSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardIntervalVariableSpec": { + "description": "Interval variable specification", + "type": "object", + "required": [ + "name", + "query", + "current", + "options", + "auto", + "auto_min", + "auto_count", + "refresh", + "hide", + "skipUrlSync" + ], + "properties": { + "auto": { + "type": "boolean", + "default": false + }, + "auto_count": { + "type": "integer", + "default": 0 + }, + "auto_min": { + "type": "string", + "default": "" + }, + "current": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + }, + "description": { + "type": "string" + }, + "hide": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableHide" + }, + "label": { + "type": "string" + }, + "name": { + "type": "string", + "default": "" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + } + }, + "query": { + "type": "string", + "default": "" + }, + "refresh": { + "type": "string" + }, + "skipUrlSync": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardLibraryPanelKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardLibraryPanelKindSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardLibraryPanelKindSpec": { + "type": "object", + "required": [ + "id", + "title", + "libraryPanel" + ], + "properties": { + "id": { + "description": "Panel ID for the library panel in the dashboard", + "type": "number" + }, + "libraryPanel": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardLibraryPanelRef" + }, + "title": { + "description": "Title for the library panel in the dashboard", + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardLibraryPanelRef": { + "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.", + "type": "object", + "required": [ + "name", + "uid" + ], + "properties": { + "name": { + "description": "Library panel name", + "type": "string" + }, + "uid": { + "description": "Library panel uid", + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardList": { + "type": "object", + "required": [ + "metadata", + "items" + ], + "properties": { + "apiVersion": { + "description": "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", + "type": "string" + }, + "items": { + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.Dashboard" + } + ] + } + }, + "kind": { + "description": "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", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "dashboard.grafana.app", + "kind": "DashboardList", + "version": "v2beta1" + } + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardMappingType": { + "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.", + "type": "string", + "enum": [ + "value", + "range", + "regex", + "special" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardMatcherConfig": { + "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.", + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "description": "The matcher id. This is used to find the matcher implementation from registry.", + "type": "string", + "default": "" + }, + "options": { + "description": "The matcher options. This is specific to the matcher implementation.", + "type": "object", + "additionalProperties": {} + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardMetricFindValue": { + "description": "Define the MetricFindValue type", + "type": "object", + "required": [ + "text" + ], + "properties": { + "expandable": { + "type": "boolean" + }, + "group": { + "type": "string" + }, + "text": { + "type": "string" + }, + "value": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + } + ] + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardPanelKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardPanelSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardPanelQueryKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardPanelQuerySpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardPanelQuerySpec": { + "type": "object", + "required": [ + "query", + "refId", + "hidden" + ], + "properties": { + "hidden": { + "type": "boolean" + }, + "query": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDataQueryKind" + }, + "refId": { + "type": "string", + "default": "A" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardPanelSpec": { + "type": "object", + "required": [ + "id", + "title", + "description", + "links", + "data", + "vizConfig" + ], + "properties": { + "data": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardQueryGroupKind" + }, + "description": { + "type": "string" + }, + "id": { + "type": "number" + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDataLink" + } + }, + "title": { + "type": "string" + }, + "transparent": { + "type": "boolean" + }, + "vizConfig": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVizConfigKind" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardQueryGroupKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardQueryGroupSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardQueryGroupSpec": { + "type": "object", + "required": [ + "queries", + "transformations", + "queryOptions" + ], + "properties": { + "queries": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardPanelQueryKind" + } + }, + "queryOptions": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardQueryOptionsSpec" + }, + "transformations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTransformationKind" + } + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardQueryOptionsSpec": { + "type": "object", + "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" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardQueryVariableKind": { + "description": "Query variable kind", + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardQueryVariableSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardQueryVariableSpec": { + "description": "Query variable specification", + "type": "object", + "required": [ + "name", + "current", + "hide", + "refresh", + "skipUrlSync", + "query", + "regex", + "sort", + "options", + "multi", + "includeAll", + "allowCustomValue" + ], + "properties": { + "allValue": { + "type": "string" + }, + "allowCustomValue": { + "type": "boolean", + "default": true + }, + "current": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + }, + "definition": { + "type": "string" + }, + "description": { + "type": "string" + }, + "hide": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableHide" + }, + "includeAll": { + "type": "boolean", + "default": false + }, + "label": { + "type": "string" + }, + "multi": { + "type": "boolean", + "default": false + }, + "name": { + "type": "string", + "default": "" + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + } + }, + "placeholder": { + "type": "string" + }, + "query": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDataQueryKind" + }, + "refresh": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableRefresh" + }, + "regex": { + "type": "string", + "default": "" + }, + "skipUrlSync": { + "type": "boolean", + "default": false + }, + "sort": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableSort" + }, + "staticOptions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + } + }, + "staticOptionsOrder": { + "type": "string", + "enum": [ + "before", + "after", + "sorted" + ] + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRangeMap": { + "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.", + "type": "object", + "required": [ + "type", + "options" + ], + "properties": { + "options": { + "description": "Range to match against and the result to apply when the value is within the range", + "type": "object", + "required": [ + "from", + "to", + "result" + ], + "properties": { + "from": { + "description": "Min value of the range. It can be null which means -Infinity", + "type": "number" + }, + "result": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardValueMappingResult" + }, + "to": { + "description": "Max value of the range. It can be null which means +Infinity", + "type": "number" + } + }, + "additionalProperties": false + }, + "type": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardMappingType" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRegexMap": { + "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.", + "type": "object", + "required": [ + "type", + "options" + ], + "properties": { + "options": { + "description": "Regular expression to match against and the result to apply when the value matches the regex", + "type": "object", + "required": [ + "pattern", + "result" + ], + "properties": { + "pattern": { + "description": "Regular expression to match against", + "type": "string" + }, + "result": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardValueMappingResult" + } + }, + "additionalProperties": false + }, + "type": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardMappingType" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRepeatMode": { + "description": "other repeat modes will be added in the future: label, frame", + "type": "string" + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRepeatOptions": { + "type": "object", + "required": [ + "mode", + "value" + ], + "properties": { + "direction": { + "type": "string", + "enum": [ + "h", + "v" + ] + }, + "maxPerRow": { + "type": "integer" + }, + "mode": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRepeatMode" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowRepeatOptions": { + "type": "object", + "required": [ + "mode", + "value" + ], + "properties": { + "mode": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRepeatMode" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowsLayoutKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowsLayoutSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowsLayoutRowKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowsLayoutRowSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowsLayoutRowSpec": { + "type": "object", + "required": [ + "layout" + ], + "properties": { + "collapse": { + "type": "boolean" + }, + "conditionalRendering": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingGroupKind" + }, + "fillScreen": { + "type": "boolean" + }, + "hideHeader": { + "type": "boolean" + }, + "layout": { + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabsLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowsLayoutKind" + } + ] + }, + "repeat": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowRepeatOptions" + }, + "title": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowsLayoutSpec": { + "type": "object", + "required": [ + "rows" + ], + "properties": { + "rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowsLayoutRowKind" + } + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardSpec": { + "type": "object", + "required": [ + "annotations", + "cursorSync", + "elements", + "layout", + "links", + "preload", + "tags", + "timeSettings", + "title", + "variables" + ], + "properties": { + "annotations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAnnotationQueryKind" + } + }, + "cursorSync": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDashboardCursorSync" + }, + "description": { + "description": "Description of dashboard.", + "type": "string" + }, + "editable": { + "description": "Whether a dashboard is editable or not.", + "type": "boolean", + "default": true + }, + "elements": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardElement" + } + }, + "layout": { + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowsLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabsLayoutKind" + } + ] + }, + "links": { + "description": "Links with references to other dashboards or external websites.", + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDashboardLink" + } + }, + "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": { + "description": "When set to true, the dashboard will load all panels in the dashboard when it's loaded.", + "type": "boolean", + "default": false + }, + "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.", + "type": "array", + "items": { + "type": "string" + } + }, + "timeSettings": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTimeSettingsSpec" + }, + "title": { + "description": "Title of dashboard.", + "type": "string" + }, + "variables": { + "description": "Configured template variables.", + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableKind" + } + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardSpecialValueMap": { + "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.", + "type": "object", + "required": [ + "type", + "options" + ], + "properties": { + "options": { + "type": "object", + "required": [ + "match", + "result" + ], + "properties": { + "match": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardSpecialValueMatch" + }, + "result": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardValueMappingResult" + } + }, + "additionalProperties": false + }, + "type": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardMappingType" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardSpecialValueMatch": { + "description": "Special value types supported by the `SpecialValueMap`", + "type": "string", + "enum": [ + "true", + "false", + "null", + "nan", + "null+nan", + "empty" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardStatus": { + "type": "object", + "properties": { + "conversion": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConversionStatus" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardSwitchVariableKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardSwitchVariableSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardSwitchVariableSpec": { + "type": "object", + "required": [ + "name", + "current", + "enabledValue", + "disabledValue", + "hide", + "skipUrlSync" + ], + "properties": { + "current": { + "type": "string", + "default": "false" + }, + "description": { + "type": "string" + }, + "disabledValue": { + "type": "string", + "default": "false" + }, + "enabledValue": { + "type": "string", + "default": "true" + }, + "hide": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableHide" + }, + "label": { + "type": "string" + }, + "name": { + "type": "string", + "default": "" + }, + "skipUrlSync": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabRepeatOptions": { + "type": "object", + "required": [ + "mode", + "value" + ], + "properties": { + "mode": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRepeatMode" + }, + "value": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabsLayoutKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabsLayoutSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabsLayoutSpec": { + "type": "object", + "required": [ + "tabs" + ], + "properties": { + "tabs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabsLayoutTabKind" + } + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabsLayoutTabKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabsLayoutTabSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabsLayoutTabSpec": { + "type": "object", + "required": [ + "layout" + ], + "properties": { + "conditionalRendering": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConditionalRenderingGroupKind" + }, + "layout": { + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRowsLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAutoGridLayoutKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabsLayoutKind" + } + ] + }, + "repeat": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTabRepeatOptions" + }, + "title": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTextVariableKind": { + "description": "Text variable kind", + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTextVariableSpec" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTextVariableSpec": { + "description": "Text variable specification", + "type": "object", + "required": [ + "name", + "current", + "query", + "hide", + "skipUrlSync" + ], + "properties": { + "current": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption" + }, + "description": { + "type": "string" + }, + "hide": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableHide" + }, + "label": { + "type": "string" + }, + "name": { + "type": "string", + "default": "" + }, + "query": { + "type": "string", + "default": "" + }, + "skipUrlSync": { + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardThreshold": { + "type": "object", + "required": [ + "value", + "color" + ], + "properties": { + "color": { + "type": "string" + }, + "value": { + "description": "Value null means -Infinity", + "type": "number" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardThresholdsConfig": { + "type": "object", + "required": [ + "mode", + "steps" + ], + "properties": { + "mode": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardThresholdsMode" + }, + "steps": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardThreshold" + } + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardThresholdsMode": { + "type": "string", + "enum": [ + "absolute", + "percentage" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTimeRangeOption": { + "type": "object", + "required": [ + "display", + "from", + "to" + ], + "properties": { + "display": { + "type": "string", + "default": "Last 6 hours" + }, + "from": { + "type": "string", + "default": "now-6h" + }, + "to": { + "type": "string", + "default": "now" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTimeSettingsSpec": { + "description": "Time configuration\nIt defines the default time config for the time picker, the refresh picker for the specific dashboard.", + "type": "object", + "required": [ + "from", + "to", + "autoRefresh", + "autoRefreshIntervals", + "hideTimepicker", + "fiscalYearStartMonth" + ], + "properties": { + "autoRefresh": { + "description": "Refresh rate of dashboard. Represented via interval string, e.g. \"5s\", \"1m\", \"1h\", \"1d\".\nv1: refresh", + "type": "string", + "default": "" + }, + "autoRefreshIntervals": { + "description": "Interval options available in the refresh picker dropdown.\nv1: timepicker.refresh_intervals", + "type": "array", + "default": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "items": { + "type": "string" + } + }, + "fiscalYearStartMonth": { + "description": "The month that the fiscal year starts on. 0 = January, 11 = December", + "type": "integer", + "default": 0 + }, + "from": { + "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", + "default": "now-6h" + }, + "hideTimepicker": { + "description": "Whether timepicker is visible or not.\nv1: timepicker.hidden", + "type": "boolean", + "default": false + }, + "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", + "type": "array", + "items": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTimeRangeOption" + } + }, + "timezone": { + "description": "Timezone of dashboard. Accepted values are IANA TZDB zone ID or \"browser\" or \"utc\".", + "type": "string", + "default": "browser" + }, + "to": { + "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", + "default": "now" + }, + "weekStart": { + "description": "Day when the week starts. Expressed by the name of the day in lowercase, e.g. \"monday\".", + "type": "string", + "enum": [ + "saturday", + "monday", + "sunday" + ] + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTransformationKind": { + "type": "object", + "required": [ + "kind", + "spec" + ], + "properties": { + "kind": { + "description": "The kind of a TransformationKind is the transformation ID", + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDataTransformerConfig" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardValueMap": { + "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.", + "type": "object", + "required": [ + "type", + "options" + ], + "properties": { + "options": { + "description": "Map with : 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" + } + }, + "type": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardMappingType" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardValueMapping": { + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardValueMap" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRangeMap" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardRegexMap" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardSpecialValueMap" + } + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardValueMappingResult": { + "description": "Result used as replacement with text and color when the value matches", + "type": "object", + "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" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableHide": { + "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).", + "type": "string", + "enum": [ + "dontHide", + "hideLabel", + "hideVariable", + "inControlsMenu" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableKind": { + "oneOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardQueryVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardTextVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardConstantVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardDatasourceVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardIntervalVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardCustomVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardGroupByVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAdhocVariableKind" + }, + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardSwitchVariableKind" + } + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableOption": { + "description": "Variable option specification", + "type": "object", + "required": [ + "text", + "value" + ], + "properties": { + "selected": { + "description": "Whether the option is selected or not", + "type": "boolean" + }, + "text": { + "description": "Text to be displayed for the option", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "value": { + "description": "Value of the option", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableRefresh": { + "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.", + "type": "string", + "enum": [ + "never", + "onDashboardLoad", + "onTimeRangeChanged" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVariableSort": { + "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", + "type": "string", + "enum": [ + "disabled", + "alphabeticalAsc", + "alphabeticalDesc", + "numericalAsc", + "numericalDesc", + "alphabeticalCaseInsensitiveAsc", + "alphabeticalCaseInsensitiveDesc", + "naturalAsc", + "naturalDesc" + ] + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVizConfigKind": { + "type": "object", + "required": [ + "kind", + "group", + "version", + "spec" + ], + "properties": { + "group": { + "description": "The group is the plugin ID", + "type": "string" + }, + "kind": { + "type": "string" + }, + "spec": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVizConfigSpec" + }, + "version": { + "type": "string" + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardVizConfigSpec": { + "description": "--- Kinds ---", + "type": "object", + "required": [ + "options", + "fieldConfig" + ], + "properties": { + "fieldConfig": { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardFieldConfigSource" + }, + "options": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": {} + } + } + }, + "additionalProperties": false + }, + "com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardWithAccessInfo": { + "description": "This is like the legacy DTO where access and metadata are all returned in a single call", + "type": "object", + "required": [ + "metadata", + "spec", + "status", + "access" + ], + "properties": { + "access": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardAccess" + } + ] + }, + "apiVersion": { + "description": "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", + "type": "string" + }, + "kind": { + "description": "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", + "type": "string" + }, + "metadata": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + ] + }, + "spec": { + "description": "Spec is the spec of the Dashboard", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardSpec" + } + ] + }, + "status": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/com.github.grafana.grafana.apps.dashboard.pkg.apis.dashboard.v2beta1.DashboardStatus" + } + ] + } + }, + "x-kubernetes-group-version-kind": [ + { + "group": "dashboard.grafana.app", + "kind": "DashboardWithAccessInfo", + "version": "v2beta1" + } + ] + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": { + "description": "APIResource specifies the name of a resource and whether it is namespaced.", + "type": "object", + "required": [ + "name", + "singularName", + "namespaced", + "kind", + "verbs" + ], + "properties": { + "categories": { + "description": "categories is a list of the grouped resources this resource belongs to (e.g. 'all')", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + }, + "group": { + "description": "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\".", + "type": "string" + }, + "kind": { + "description": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')", + "type": "string", + "default": "" + }, + "name": { + "description": "name is the plural name of the resource.", + "type": "string", + "default": "" + }, + "namespaced": { + "description": "namespaced indicates if a resource is namespaced or not.", + "type": "boolean", + "default": false + }, + "shortNames": { + "description": "shortNames is a list of suggested short names of the resource.", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + }, + "singularName": { + "description": "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.", + "type": "string", + "default": "" + }, + "storageVersionHash": { + "description": "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.", + "type": "string" + }, + "verbs": { + "description": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)", + "type": "array", + "items": { + "type": "string", + "default": "" + } + }, + "version": { + "description": "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)\".", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.APIResourceList": { + "description": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.", + "type": "object", + "required": [ + "groupVersion", + "resources" + ], + "properties": { + "apiVersion": { + "description": "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", + "type": "string" + }, + "groupVersion": { + "description": "groupVersion is the group and version this APIResourceList is for.", + "type": "string", + "default": "" + }, + "kind": { + "description": "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", + "type": "string" + }, + "resources": { + "description": "resources contains the name of the resources and if they are namespaced.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.APIResource" + } + ] + }, + "x-kubernetes-list-type": "atomic" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": { + "description": "DeleteOptions may be provided when deleting an API object.", + "type": "object", + "properties": { + "apiVersion": { + "description": "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", + "type": "string" + }, + "dryRun": { + "description": "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", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "atomic" + }, + "gracePeriodSeconds": { + "description": "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.", + "type": "integer", + "format": "int64" + }, + "ignoreStoreReadErrorWithClusterBreakingPotential": { + "description": "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", + "type": "boolean" + }, + "kind": { + "description": "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", + "type": "string" + }, + "orphanDependents": { + "description": "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.", + "type": "boolean" + }, + "preconditions": { + "description": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions" + } + ] + }, + "propagationPolicy": { + "description": "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.", + "type": "string" + } + } + }, + "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", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": { + "description": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.", + "type": "object", + "properties": { + "continue": { + "description": "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.", + "type": "string" + }, + "remainingItemCount": { + "description": "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.", + "type": "integer", + "format": "int64" + }, + "resourceVersion": { + "description": "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", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry": { + "description": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", + "type": "object", + "properties": { + "apiVersion": { + "description": "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.", + "type": "string" + }, + "fieldsType": { + "description": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", + "type": "string" + }, + "fieldsV1": { + "description": "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type.", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1" + } + ] + }, + "manager": { + "description": "Manager is an identifier of the workflow managing these fields.", + "type": "string" + }, + "operation": { + "description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", + "type": "string" + }, + "subresource": { + "description": "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.", + "type": "string" + }, + "time": { + "description": "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.", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ] + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta": { + "description": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.", + "type": "object", + "properties": { + "annotations": { + "description": "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", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "creationTimestamp": { + "description": "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.\n\nPopulated by the system. Read-only. Null for lists. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ] + }, + "deletionGracePeriodSeconds": { + "description": "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.", + "type": "integer", + "format": "int64" + }, + "deletionTimestamp": { + "description": "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.\n\nPopulated 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", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time" + } + ] + }, + "finalizers": { + "description": "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.", + "type": "array", + "items": { + "type": "string", + "default": "" + }, + "x-kubernetes-list-type": "set", + "x-kubernetes-patch-strategy": "merge" + }, + "generateName": { + "description": "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.\n\nIf this field is specified and the generated name exists, the server will return a 409.\n\nApplied only if Name is not specified. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency", + "type": "string" + }, + "generation": { + "description": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.", + "type": "integer", + "format": "int64" + }, + "labels": { + "description": "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", + "type": "object", + "additionalProperties": { + "type": "string", + "default": "" + } + }, + "managedFields": { + "description": "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.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "name": { + "description": "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", + "type": "string" + }, + "namespace": { + "description": "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.\n\nMust be a DNS_LABEL. Cannot be updated. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces", + "type": "string" + }, + "ownerReferences": { + "description": "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.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference" + } + ] + }, + "x-kubernetes-list-map-keys": [ + "uid" + ], + "x-kubernetes-list-type": "map", + "x-kubernetes-patch-merge-key": "uid", + "x-kubernetes-patch-strategy": "merge" + }, + "resourceVersion": { + "description": "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.\n\nPopulated 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", + "type": "string" + }, + "selfLink": { + "description": "Deprecated: selfLink is a legacy read-only field that is no longer populated by the system.", + "type": "string" + }, + "uid": { + "description": "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.\n\nPopulated by the system. Read-only. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference": { + "description": "OwnerReference contains enough information to let you identify an owning object. An owning object must be in the same namespace as the dependent, or be cluster-scoped, so there is no namespace field.", + "type": "object", + "required": [ + "apiVersion", + "kind", + "name", + "uid" + ], + "properties": { + "apiVersion": { + "description": "API version of the referent.", + "type": "string", + "default": "" + }, + "blockOwnerDeletion": { + "description": "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.", + "type": "boolean" + }, + "controller": { + "description": "If true, this reference points to the managing controller.", + "type": "boolean" + }, + "kind": { + "description": "Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "type": "string", + "default": "" + }, + "name": { + "description": "Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#names", + "type": "string", + "default": "" + }, + "uid": { + "description": "UID of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#uids", + "type": "string", + "default": "" + } + }, + "x-kubernetes-map-type": "atomic" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Patch": { + "description": "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.", + "type": "object" + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Preconditions": { + "description": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.", + "type": "object", + "properties": { + "resourceVersion": { + "description": "Specifies the target ResourceVersion", + "type": "string" + }, + "uid": { + "description": "Specifies the target UID.", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Status": { + "description": "Status is a return value for calls that don't return other objects.", + "type": "object", + "properties": { + "apiVersion": { + "description": "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", + "type": "string" + }, + "code": { + "description": "Suggested HTTP return code for this status, 0 if not set.", + "type": "integer", + "format": "int32" + }, + "details": { + "description": "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.", + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails" + } + ], + "x-kubernetes-list-type": "atomic" + }, + "kind": { + "description": "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", + "type": "string" + }, + "message": { + "description": "A human-readable description of the status of this operation.", + "type": "string" + }, + "metadata": { + "description": "Standard list metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" + } + ] + }, + "reason": { + "description": "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.", + "type": "string" + }, + "status": { + "description": "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", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause": { + "description": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.", + "type": "object", + "properties": { + "field": { + "description": "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.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"", + "type": "string" + }, + "message": { + "description": "A human-readable description of the cause of the error. This field may be presented as-is to a reader.", + "type": "string" + }, + "reason": { + "description": "A machine-readable description of the cause of the error. If this value is empty there is no information available.", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.StatusDetails": { + "description": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.", + "type": "object", + "properties": { + "causes": { + "description": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.", + "type": "array", + "items": { + "default": {}, + "allOf": [ + { + "$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.StatusCause" + } + ] + }, + "x-kubernetes-list-type": "atomic" + }, + "group": { + "description": "The group attribute of the resource associated with the status StatusReason.", + "type": "string" + }, + "kind": { + "description": "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", + "type": "string" + }, + "name": { + "description": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).", + "type": "string" + }, + "retryAfterSeconds": { + "description": "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.", + "type": "integer", + "format": "int32" + }, + "uid": { + "description": "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", + "type": "string" + } + } + }, + "io.k8s.apimachinery.pkg.apis.meta.v1.Time": { + "description": "Time is a wrapper around time.Time which supports correct marshaling to YAML and JSON. Wrappers are provided for many of the factory methods that the time package offers.", + "type": "string", + "format": "date-time" + } + } + } +} diff --git a/pkg/tests/apis/openapi_test.go b/pkg/tests/apis/openapi_test.go index 3e02a985eb1..b9d0283e37a 100644 --- a/pkg/tests/apis/openapi_test.go +++ b/pkg/tests/apis/openapi_test.go @@ -78,6 +78,9 @@ func TestIntegrationOpenAPIs(t *testing.T) { }, { Group: "dashboard.grafana.app", Version: "v2alpha1", + }, { + Group: "dashboard.grafana.app", + Version: "v2beta1", }, { Group: "folder.grafana.app", Version: "v1beta1", From 592c599ca6d6e7c000ee98c6b0b1a93d30b02d53 Mon Sep 17 00:00:00 2001 From: Steve Simpson Date: Sat, 6 Dec 2025 10:37:20 +0100 Subject: [PATCH 35/48] Alerting: Add configurable transport to historian app (#114935) --- .../historian/pkg/app/config/config.go | 10 ++++-- .../historian/pkg/app/config/config_test.go | 36 +++++++++++-------- .../pkg/app/notification/lokireader.go | 10 ++++-- .../apps/alerting/historian/register.go | 4 ++- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/apps/alerting/historian/pkg/app/config/config.go b/apps/alerting/historian/pkg/app/config/config.go index 5d8027d933b..cb3b3caa711 100644 --- a/apps/alerting/historian/pkg/app/config/config.go +++ b/apps/alerting/historian/pkg/app/config/config.go @@ -1,6 +1,7 @@ package config import ( + "net/http" "net/url" "time" @@ -15,9 +16,14 @@ const ( lokiDefaultMaxQuerySize = 65536 // 64kb ) +type LokiConfig struct { + lokiclient.LokiConfig + Transport http.RoundTripper +} + type NotificationConfig struct { Enabled bool - Loki lokiclient.LokiConfig + Loki LokiConfig } type RuntimeConfig struct { @@ -27,7 +33,7 @@ type RuntimeConfig struct { func (n *NotificationConfig) AddFlagsWithPrefix(prefix string, flags *pflag.FlagSet) { flags.BoolVar(&n.Enabled, prefix+".enabled", false, "Enable notification query endpoints") - addLokiFlags(&n.Loki, prefix+".loki", flags) + addLokiFlags(&n.Loki.LokiConfig, prefix+".loki", flags) } func (r *RuntimeConfig) AddFlagsWithPrefix(prefix string, flags *pflag.FlagSet) { diff --git a/apps/alerting/historian/pkg/app/config/config_test.go b/apps/alerting/historian/pkg/app/config/config_test.go index 8234f4c945f..7f8ab623a7a 100644 --- a/apps/alerting/historian/pkg/app/config/config_test.go +++ b/apps/alerting/historian/pkg/app/config/config_test.go @@ -24,10 +24,12 @@ func TestRuntimeConfig(t *testing.T) { expected: RuntimeConfig{ Notification: NotificationConfig{ Enabled: false, - Loki: lokiclient.LokiConfig{ - ReadPathURL: nil, - MaxQueryLength: 721 * time.Hour, - MaxQuerySize: 65536, + Loki: LokiConfig{ + LokiConfig: lokiclient.LokiConfig{ + ReadPathURL: nil, + MaxQueryLength: 721 * time.Hour, + MaxQuerySize: 65536, + }, }, }, }, @@ -38,10 +40,12 @@ func TestRuntimeConfig(t *testing.T) { expected: RuntimeConfig{ Notification: NotificationConfig{ Enabled: true, - Loki: lokiclient.LokiConfig{ - ReadPathURL: nil, - MaxQueryLength: 721 * time.Hour, - MaxQuerySize: 65536, + Loki: LokiConfig{ + LokiConfig: lokiclient.LokiConfig{ + ReadPathURL: nil, + MaxQueryLength: 721 * time.Hour, + MaxQuerySize: 65536, + }, }, }, }, @@ -57,13 +61,15 @@ func TestRuntimeConfig(t *testing.T) { expected: RuntimeConfig{ Notification: NotificationConfig{ Enabled: false, - Loki: lokiclient.LokiConfig{ - ReadPathURL: lokiURL, - BasicAuthUser: "foo", - BasicAuthPassword: "bar", - TenantID: "baz", - MaxQueryLength: 721 * time.Hour, - MaxQuerySize: 65536, + Loki: LokiConfig{ + LokiConfig: lokiclient.LokiConfig{ + ReadPathURL: lokiURL, + BasicAuthUser: "foo", + BasicAuthPassword: "bar", + TenantID: "baz", + MaxQueryLength: 721 * time.Hour, + MaxQuerySize: 65536, + }, }, }, }, diff --git a/apps/alerting/historian/pkg/app/notification/lokireader.go b/apps/alerting/historian/pkg/app/notification/lokireader.go index e8cea23dda7..c26519e59b4 100644 --- a/apps/alerting/historian/pkg/app/notification/lokireader.go +++ b/apps/alerting/historian/pkg/app/notification/lokireader.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "net/http" "regexp" "sort" "strings" @@ -19,6 +20,7 @@ import ( "go.opentelemetry.io/otel/trace" "github.com/grafana/grafana/apps/alerting/historian/pkg/apis/alertinghistorian/v0alpha1" + "github.com/grafana/grafana/apps/alerting/historian/pkg/app/config" "github.com/grafana/grafana/apps/alerting/historian/pkg/app/logutil" ) @@ -47,7 +49,7 @@ type LokiReader struct { logger logging.Logger } -func NewLokiReader(cfg lokiclient.LokiConfig, reg prometheus.Registerer, logger logging.Logger, tracer trace.Tracer) *LokiReader { +func NewLokiReader(cfg config.LokiConfig, reg prometheus.Registerer, logger logging.Logger, tracer trace.Tracer) *LokiReader { duration := instrument.NewHistogramCollector(promauto.With(reg).NewHistogramVec(prometheus.HistogramOpts{ Namespace: Namespace, Subsystem: Subsystem, @@ -56,9 +58,13 @@ func NewLokiReader(cfg lokiclient.LokiConfig, reg prometheus.Registerer, logger Buckets: instrument.DefBuckets, }, instrument.HistogramCollectorBuckets)) + requester := &http.Client{ + Transport: cfg.Transport, + } + gkLogger := logutil.ToGoKitLogger(logger) return &LokiReader{ - client: lokiclient.NewLokiClient(cfg, lokiclient.NewRequester(), nil, duration, gkLogger, tracer, LokiClientSpanName), + client: lokiclient.NewLokiClient(cfg.LokiConfig, requester, nil, duration, gkLogger, tracer, LokiClientSpanName), logger: logger, } } diff --git a/pkg/registry/apps/alerting/historian/register.go b/pkg/registry/apps/alerting/historian/register.go index 7fc2176d758..68830dcd0ef 100644 --- a/pkg/registry/apps/alerting/historian/register.go +++ b/pkg/registry/apps/alerting/historian/register.go @@ -42,7 +42,9 @@ func RegisterAppInstaller( appSpecificConfig.Notification = historianAppConfig.NotificationConfig{ Enabled: nhCfg.Enabled, - Loki: lokiConfig, + Loki: historianAppConfig.LokiConfig{ + LokiConfig: lokiConfig, + }, } } } From 78b1ae4f27c0d8ae5473faf55523da791bfccbff Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Sat, 6 Dec 2025 16:45:18 +0300 Subject: [PATCH 36/48] Search: Fix field selector parsing (#114940) --- pkg/storage/unified/apistore/util.go | 2 +- pkg/storage/unified/apistore/util_test.go | 43 +++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/pkg/storage/unified/apistore/util.go b/pkg/storage/unified/apistore/util.go index d3763f652a6..6cb4c5b31f8 100644 --- a/pkg/storage/unified/apistore/util.go +++ b/pkg/storage/unified/apistore/util.go @@ -124,7 +124,7 @@ func toListRequest(k *resourcepb.ResourceKey, opts storage.ListOptions) (*resour if r.Value != "" { requirement.Values = append(requirement.Values, r.Value) } - req.Options.Labels = append(req.Options.Labels, requirement) + req.Options.Fields = append(req.Options.Fields, requirement) } } diff --git a/pkg/storage/unified/apistore/util_test.go b/pkg/storage/unified/apistore/util_test.go index 0bee8adf75e..68ca1ad462f 100644 --- a/pkg/storage/unified/apistore/util_test.go +++ b/pkg/storage/unified/apistore/util_test.go @@ -117,6 +117,49 @@ func TestToListRequest(t *testing.T) { }, wantErr: nil, }, + { + name: "with field selector", + key: &resourcepb.ResourceKey{ + Group: "test", + Resource: "test", + Namespace: "default", + }, + opts: storage.ListOptions{ + Predicate: storage.SelectionPredicate{ + Label: labels.SelectorFromSet(labels.Set{"label": "A"}), + Field: fields.SelectorFromSet(fields.Set{"field": "B"}), + }, + }, + want: &resourcepb.ListRequest{ + VersionMatchV2: 1, + Options: &resourcepb.ListOptions{ + Key: &resourcepb.ResourceKey{ + Group: "test", + Resource: "test", + Namespace: "default", + }, + Labels: []*resourcepb.Requirement{ + { + Key: "label", + Operator: string(selection.Equals), + Values: []string{"A"}, + }, + }, + Fields: []*resourcepb.Requirement{ + { + Key: "field", + Operator: string(selection.Equals), + Values: []string{"B"}, + }, + }, + }, + }, + wantPredicate: storage.SelectionPredicate{ + Label: labels.SelectorFromSet(labels.Set{"label": "A"}), + Field: fields.SelectorFromSet(fields.Set{"field": "B"}), + }, + wantErr: nil, + }, { name: "with trash label", key: &resourcepb.ResourceKey{ From d0977b524561d8bfcc20687f0d10cd5e2c5a0754 Mon Sep 17 00:00:00 2001 From: Gabriel MABILLE Date: Mon, 8 Dec 2025 09:22:28 +0100 Subject: [PATCH 37/48] `grafana-iam`: Add role apis to the standalone app (#114897) --- pkg/registry/apis/iam/authorizer.go | 2 +- pkg/registry/apis/iam/register.go | 41 ++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/pkg/registry/apis/iam/authorizer.go b/pkg/registry/apis/iam/authorizer.go index 0ec018d86de..05c8da97c2e 100644 --- a/pkg/registry/apis/iam/authorizer.go +++ b/pkg/registry/apis/iam/authorizer.go @@ -44,7 +44,7 @@ func newIAMAuthorizer(accessClient authlib.AccessClient, legacyAccessClient auth authorizer := gfauthorizer.NewResourceAuthorizer(accessClient) resourceAuthorizer[iamv0.CoreRoleInfo.GetName()] = iamauthorizer.NewCoreRoleAuthorizer(accessClient) resourceAuthorizer[iamv0.RoleInfo.GetName()] = authorizer - resourceAuthorizer[iamv0.ResourcePermissionInfo.GetName()] = allowAuthorizer // Handled at storage layer + resourceAuthorizer[iamv0.ResourcePermissionInfo.GetName()] = allowAuthorizer // Handled by the backend wrapper resourceAuthorizer[iamv0.RoleBindingInfo.GetName()] = authorizer resourceAuthorizer[iamv0.ServiceAccountResourceInfo.GetName()] = authorizer resourceAuthorizer[iamv0.UserResourceInfo.GetName()] = authorizer diff --git a/pkg/registry/apis/iam/register.go b/pkg/registry/apis/iam/register.go index 786635fa19a..32e2c9fefef 100644 --- a/pkg/registry/apis/iam/register.go +++ b/pkg/registry/apis/iam/register.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/prometheus/client_golang/prometheus" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -23,7 +24,6 @@ import ( "github.com/grafana/authlib/types" iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1" - "github.com/grafana/grafana/pkg/apimachinery/identity" "github.com/grafana/grafana/pkg/apimachinery/utils" legacyiamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" @@ -32,6 +32,7 @@ import ( iamauthorizer "github.com/grafana/grafana/pkg/registry/apis/iam/authorizer" "github.com/grafana/grafana/pkg/registry/apis/iam/externalgroupmapping" "github.com/grafana/grafana/pkg/registry/apis/iam/legacy" + "github.com/grafana/grafana/pkg/registry/apis/iam/noopstorage" "github.com/grafana/grafana/pkg/registry/apis/iam/resourcepermission" "github.com/grafana/grafana/pkg/registry/apis/iam/serviceaccount" "github.com/grafana/grafana/pkg/registry/apis/iam/sso" @@ -39,6 +40,7 @@ import ( "github.com/grafana/grafana/pkg/registry/apis/iam/teambinding" "github.com/grafana/grafana/pkg/registry/apis/iam/user" "github.com/grafana/grafana/pkg/services/accesscontrol" + gfauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer" "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer/storewrapper" "github.com/grafana/grafana/pkg/services/apiserver/builder" "github.com/grafana/grafana/pkg/services/authz/zanzana" @@ -116,6 +118,8 @@ func RegisterAPIService( func NewAPIService( accessClient types.AccessClient, dbProvider legacysql.LegacyDatabaseProvider, + coreRoleStorage CoreRoleStorageBackend, + roleStorage RoleStorageBackend, features featuremgmt.FeatureToggles, zClient zanzana.Client, reg prometheus.Registerer, @@ -123,10 +127,17 @@ func NewAPIService( store := legacy.NewLegacySQLStores(dbProvider) resourcePermissionsStorage := resourcepermission.ProvideStorageBackend(dbProvider) registerMetrics(reg) + + resourceAuthorizer := gfauthorizer.NewResourceAuthorizer(accessClient) + coreRoleAuthorizer := iamauthorizer.NewCoreRoleAuthorizer(accessClient) + return &IdentityAccessManagementAPIBuilder{ store: store, display: user.NewLegacyDisplayREST(store), resourcePermissionsStorage: resourcePermissionsStorage, + rolesStorage: roleStorage, + coreRolesStorage: coreRoleStorage, + roleBindingsStorage: noopstorage.ProvideStorageBackend(), // TODO: add a proper storage backend logger: log.New("iam.apis"), features: features, accessClient: accessClient, @@ -135,20 +146,32 @@ func NewAPIService( reg: reg, authorizer: authorizer.AuthorizerFunc( func(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) { + user, ok := types.AuthInfoFrom(ctx) + if !ok { + return authorizer.DecisionDeny, "no identity found", apierrors.NewUnauthorized("no identity found in context") + } + + if a.GetResource() == "coreroles" { + if user.GetIdentityType() != types.TypeAccessPolicy { + return authorizer.DecisionDeny, "only access policy identities have access for now", nil + } + return coreRoleAuthorizer.Authorize(ctx, a) + } + // For now only authorize resourcepermissions resource if a.GetResource() == "resourcepermissions" { - // Authorization is handled at the storage layer + // Authorization is handled by the backend wrapper return authorizer.DecisionAllow, "", nil } - user, err := identity.GetRequester(ctx) - if err != nil { - return authorizer.DecisionDeny, "no identity found", err + if a.GetResource() == "roles" { + if user.GetIdentityType() != types.TypeAccessPolicy { + return authorizer.DecisionDeny, "only access policy identities have access for now", nil + } + return resourceAuthorizer.Authorize(ctx, a) } - if user.GetIsGrafanaAdmin() { - return authorizer.DecisionAllow, "", nil - } - return authorizer.DecisionDeny, "only grafana admins have access for now", nil + + return authorizer.DecisionDeny, "access denied", nil }), } } From 8bf3ac97108cd826c76a845d1544541a6e632622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 8 Dec 2025 10:13:56 +0100 Subject: [PATCH 38/48] SelectBase: Use standard portal container (#114844) * SelectBase: Use standard portal container * Fixed positioning issue --- packages/grafana-ui/src/components/Select/SelectBase.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/grafana-ui/src/components/Select/SelectBase.tsx b/packages/grafana-ui/src/components/Select/SelectBase.tsx index 8609c307c25..cad0eb10e20 100644 --- a/packages/grafana-ui/src/components/Select/SelectBase.tsx +++ b/packages/grafana-ui/src/components/Select/SelectBase.tsx @@ -16,6 +16,7 @@ import { t, Trans } from '@grafana/i18n'; import { useTheme2 } from '../../themes/ThemeContext'; import { Icon } from '../Icon/Icon'; +import { getPortalContainer } from '../Portal/Portal'; import { CustomInput } from './CustomInput'; import { DropdownIndicator } from './DropdownIndicator'; @@ -123,7 +124,7 @@ export function SelectBase({ minMenuHeight, maxVisibleValues, menuPlacement = 'auto', - menuPosition, + menuPosition = 'fixed', menuShouldPortal = true, noOptionsMessage = t('grafana-ui.select.no-options-label', 'No options found'), onBlur, @@ -255,9 +256,9 @@ export function SelectBase({ maxVisibleValues, menuIsOpen: isOpen, menuPlacement: menuPlacement === 'auto' && closeToBottom ? 'top' : menuPlacement, - menuPosition, + menuPosition: menuShouldPortal ? 'fixed' : menuPosition, menuShouldBlockScroll: true, - menuPortalTarget: menuShouldPortal && typeof document !== 'undefined' ? document.body : undefined, + menuPortalTarget: menuShouldPortal && getPortalContainer(), menuShouldScrollIntoView: false, onBlur, onChange: onChangeWithEmpty, From 3490c3b0fdc8cab4fc40ccce89b4be9d82aec6d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=A4ggmark?= Date: Mon, 8 Dec 2025 10:19:44 +0100 Subject: [PATCH 39/48] e2e: add tests for translations (#114390) e2e: add tests for translations --- devenv/plugins.yaml | 4 + .../components/App/App.tsx | 3 +- .../grafana-extensionstest-app/constants.ts | 1 + .../i18next.config.ts | 13 +++ .../en-US/grafana-extensionstest-app.json | 7 ++ .../es-ES/grafana-extensionstest-app.json | 7 ++ .../sv-SE/grafana-extensionstest-app.json | 7 ++ .../grafana-extensionstest-app/module.tsx | 3 + .../grafana-extensionstest-app/package.json | 10 ++- .../pages/Config.tsx | 17 ++++ .../pages/index.tsx | 1 + .../grafana-extensionstest-app/plugin.json | 5 +- .../tests/translations/french.spec.ts | 12 +++ .../tests/translations/swedish.spec.ts | 12 +++ .../webpack.config.ts | 1 + .../components/ConfigEditor.tsx | 27 ++++-- .../grafana-test-datasource/i18next.config.ts | 13 +++ .../en-US/grafana-e2etest-datasource.json | 23 +++++ .../es-ES/grafana-e2etest-datasource.json | 23 +++++ .../sv-SE/grafana-e2etest-datasource.json | 23 +++++ .../grafana-test-datasource/module.ts | 4 + .../grafana-test-datasource/package.json | 9 +- .../grafana-test-datasource/plugin.json | 5 +- .../tests/translations/french.spec.ts | 11 +++ .../tests/translations/swedish.spec.ts | 11 +++ .../grafana-test-datasource/webpack.config.ts | 1 + .../grafana-test-panel/CHANGELOG.md | 1 + .../test-plugins/grafana-test-panel/README.md | 0 .../components/SimplePanel.tsx | 83 +++++++++++++++++++ .../grafana-test-panel/i18next.config.ts | 13 +++ .../grafana-test-panel/img/logo.svg | 1 + .../locales/en-US/grafana-e2etest-panel.json | 30 +++++++ .../locales/es-ES/grafana-e2etest-panel.json | 30 +++++++ .../locales/sv-SE/grafana-e2etest-panel.json | 30 +++++++ .../test-plugins/grafana-test-panel/module.ts | 46 ++++++++++ .../grafana-test-panel/package.json | 50 +++++++++++ .../grafana-test-panel/plugin.json | 26 ++++++ .../tests/translations/french.spec.ts | 13 +++ .../tests/translations/swedish.spec.ts | 13 +++ .../grafana-test-panel/tsconfig.json | 8 ++ .../test-plugins/grafana-test-panel/types.ts | 7 ++ .../grafana-test-panel/webpack.config.ts | 45 ++++++++++ pkg/build/e2e-playwright/main.go | 5 ++ playwright.config.ts | 4 + scripts/grafana-server/custom.ini | 2 +- yarn.lock | 38 +++++++++ 46 files changed, 678 insertions(+), 20 deletions(-) create mode 100644 e2e-playwright/test-plugins/grafana-extensionstest-app/i18next.config.ts create mode 100644 e2e-playwright/test-plugins/grafana-extensionstest-app/locales/en-US/grafana-extensionstest-app.json create mode 100644 e2e-playwright/test-plugins/grafana-extensionstest-app/locales/es-ES/grafana-extensionstest-app.json create mode 100644 e2e-playwright/test-plugins/grafana-extensionstest-app/locales/sv-SE/grafana-extensionstest-app.json create mode 100644 e2e-playwright/test-plugins/grafana-extensionstest-app/pages/Config.tsx create mode 100644 e2e-playwright/test-plugins/grafana-extensionstest-app/tests/translations/french.spec.ts create mode 100644 e2e-playwright/test-plugins/grafana-extensionstest-app/tests/translations/swedish.spec.ts create mode 100644 e2e-playwright/test-plugins/grafana-test-datasource/i18next.config.ts create mode 100644 e2e-playwright/test-plugins/grafana-test-datasource/locales/en-US/grafana-e2etest-datasource.json create mode 100644 e2e-playwright/test-plugins/grafana-test-datasource/locales/es-ES/grafana-e2etest-datasource.json create mode 100644 e2e-playwright/test-plugins/grafana-test-datasource/locales/sv-SE/grafana-e2etest-datasource.json create mode 100644 e2e-playwright/test-plugins/grafana-test-datasource/tests/translations/french.spec.ts create mode 100644 e2e-playwright/test-plugins/grafana-test-datasource/tests/translations/swedish.spec.ts create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/CHANGELOG.md create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/README.md create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/components/SimplePanel.tsx create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/i18next.config.ts create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/img/logo.svg create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/locales/en-US/grafana-e2etest-panel.json create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/locales/es-ES/grafana-e2etest-panel.json create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/locales/sv-SE/grafana-e2etest-panel.json create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/module.ts create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/package.json create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/plugin.json create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/tests/translations/french.spec.ts create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/tests/translations/swedish.spec.ts create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/tsconfig.json create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/types.ts create mode 100644 e2e-playwright/test-plugins/grafana-test-panel/webpack.config.ts diff --git a/devenv/plugins.yaml b/devenv/plugins.yaml index 554a4828cff..0f292e324f1 100644 --- a/devenv/plugins.yaml +++ b/devenv/plugins.yaml @@ -21,3 +21,7 @@ apps: org_id: 1 org_name: Main Org. disabled: false +panels: + - type: grafana-e2etest-panel + org_id: 1 + org_name: Main Org. diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/components/App/App.tsx b/e2e-playwright/test-plugins/grafana-extensionstest-app/components/App/App.tsx index d57a1476e19..26d7b201466 100644 --- a/e2e-playwright/test-plugins/grafana-extensionstest-app/components/App/App.tsx +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/components/App/App.tsx @@ -3,7 +3,7 @@ import { Route, Routes } from 'react-router-dom'; import { AppRootProps } from '@grafana/data'; import { ROUTES } from '../../constants'; -import { AddedComponents, AddedLinks, ExposedComponents } from '../../pages'; +import { AddedComponents, AddedLinks, Config, ExposedComponents } from '../../pages'; import { testIds } from '../../testIds'; export function App(props: AppRootProps) { @@ -13,6 +13,7 @@ export function App(props: AppRootProps) { } /> } /> } /> + } /> } /> diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/constants.ts b/e2e-playwright/test-plugins/grafana-extensionstest-app/constants.ts index 120eb5d8191..c208781acf3 100644 --- a/e2e-playwright/test-plugins/grafana-extensionstest-app/constants.ts +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/constants.ts @@ -8,4 +8,5 @@ export enum ROUTES { ExposedComponents = 'exposed-components', AddedComponents = 'added-components', AddedLinks = 'added-links', + Config = 'config', } diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/i18next.config.ts b/e2e-playwright/test-plugins/grafana-extensionstest-app/i18next.config.ts new file mode 100644 index 00000000000..ba1645d38c9 --- /dev/null +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/i18next.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'i18next-cli'; +import pluginJson from './plugin.json'; + +export default defineConfig({ + locales: pluginJson.languages, + extract: { + input: ['**/*.{tsx,ts}'], + output: 'locales/{{language}}/{{namespace}}.json', + defaultNS: pluginJson.id, + functions: ['t', '*.t'], + transComponents: ['Trans'], + }, +}); diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/locales/en-US/grafana-extensionstest-app.json b/e2e-playwright/test-plugins/grafana-extensionstest-app/locales/en-US/grafana-extensionstest-app.json new file mode 100644 index 00000000000..2fc28958864 --- /dev/null +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/locales/en-US/grafana-extensionstest-app.json @@ -0,0 +1,7 @@ +{ + "config-page": { + "header": { + "text": "Is this translated" + } + } +} diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/locales/es-ES/grafana-extensionstest-app.json b/e2e-playwright/test-plugins/grafana-extensionstest-app/locales/es-ES/grafana-extensionstest-app.json new file mode 100644 index 00000000000..2c2f51a239d --- /dev/null +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/locales/es-ES/grafana-extensionstest-app.json @@ -0,0 +1,7 @@ +{ + "config-page": { + "header": { + "text": "¿Está traducido?" + } + } +} diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/locales/sv-SE/grafana-extensionstest-app.json b/e2e-playwright/test-plugins/grafana-extensionstest-app/locales/sv-SE/grafana-extensionstest-app.json new file mode 100644 index 00000000000..8bae86f58aa --- /dev/null +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/locales/sv-SE/grafana-extensionstest-app.json @@ -0,0 +1,7 @@ +{ + "config-page": { + "header": { + "text": "Det här är översatt" + } + } +} diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/module.tsx b/e2e-playwright/test-plugins/grafana-extensionstest-app/module.tsx index 89ed1af12c4..9f585c0d5f7 100644 --- a/e2e-playwright/test-plugins/grafana-extensionstest-app/module.tsx +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/module.tsx @@ -3,6 +3,9 @@ import { App } from './components/App'; import { QueryModal } from './components/QueryModal'; import { selectQuery } from './utils/utils'; import pluginJson from './plugin.json'; +import { initPluginTranslations } from '@grafana/i18n'; + +await initPluginTranslations(pluginJson.id); export const plugin = new AppPlugin<{}>() .setRootPage(App) diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/package.json b/e2e-playwright/test-plugins/grafana-extensionstest-app/package.json index f89721aaac9..7f094c82b9b 100644 --- a/e2e-playwright/test-plugins/grafana-extensionstest-app/package.json +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/package.json @@ -6,7 +6,8 @@ "build": "NODE_OPTIONS='--experimental-strip-types --no-warnings=ExperimentalWarning' webpack -c ./webpack.config.ts --env production", "dev": "NODE_OPTIONS='--experimental-strip-types --no-warnings=ExperimentalWarning' webpack -w -c ./webpack.config.ts --env development", "typecheck": "tsc --noEmit", - "lint": "eslint --cache --ignore-path ./.gitignore --ext .js,.jsx,.ts,.tsx ." + "lint": "eslint --cache --ignore-path ./.gitignore --ext .js,.jsx,.ts,.tsx .", + "i18n-extract": "i18next-cli extract --sync-primary" }, "author": "Grafana Labs", "license": "Apache-2.0", @@ -20,17 +21,19 @@ "@types/semver": "7.5.8", "@types/uuid": "9.0.8", "glob": "10.5.0", + "i18next-cli": "^1.24.22", "ts-node": "10.9.2", "typescript": "5.5.4", "webpack": "5.95.0", "webpack-merge": "5.10.0" }, "engines": { - "node": ">=20" + "node": ">= 22 <25" }, "dependencies": { "@emotion/css": "11.11.2", "@grafana/data": "workspace:*", + "@grafana/i18n": "workspace:*", "@grafana/runtime": "workspace:*", "@grafana/schema": "workspace:*", "@grafana/ui": "workspace:*", @@ -42,5 +45,6 @@ }, "peerDependencies": { "@grafana/runtime": "*" - } + }, + "packageManager": "yarn@4.11.0" } diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/pages/Config.tsx b/e2e-playwright/test-plugins/grafana-extensionstest-app/pages/Config.tsx new file mode 100644 index 00000000000..8b31e490242 --- /dev/null +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/pages/Config.tsx @@ -0,0 +1,17 @@ +import { Trans } from '@grafana/i18n'; +import { PluginPage } from '@grafana/runtime'; +import { Stack } from '@grafana/ui'; + +export function Config() { + return ( + + +
    +

    + Is this translated +

    +
    +
    +
    + ); +} diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/pages/index.tsx b/e2e-playwright/test-plugins/grafana-extensionstest-app/pages/index.tsx index 1326d3c7bdf..84ddfc8e6ea 100644 --- a/e2e-playwright/test-plugins/grafana-extensionstest-app/pages/index.tsx +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/pages/index.tsx @@ -1,3 +1,4 @@ export { ExposedComponents } from './ExposedComponents'; export { AddedComponents } from './AddedComponents'; export { AddedLinks } from './AddedLinks'; +export { Config } from './Config'; diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/plugin.json b/e2e-playwright/test-plugins/grafana-extensionstest-app/plugin.json index 3f5adfa215a..c5ce29f2dc4 100644 --- a/e2e-playwright/test-plugins/grafana-extensionstest-app/plugin.json +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/plugin.json @@ -82,10 +82,11 @@ ] }, "dependencies": { - "grafanaDependency": ">=10.4.0", + "grafanaDependency": ">=12.0.0", "plugins": [], "extensions": { "exposedComponents": ["grafana-extensionexample1-app/reusable-component/v1", "grafana/add-to-dashboard-form/v1"] } - } + }, + "languages": ["en-US", "es-ES", "sv-SE"] } diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/tests/translations/french.spec.ts b/e2e-playwright/test-plugins/grafana-extensionstest-app/tests/translations/french.spec.ts new file mode 100644 index 00000000000..a991453b0cc --- /dev/null +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/tests/translations/french.spec.ts @@ -0,0 +1,12 @@ +import { FRENCH_FRANCE } from '@grafana/i18n'; +import { expect, test } from '@grafana/plugin-e2e'; +import pluginJson from '../../plugin.json'; +import { ROUTES } from '../../constants'; + +test.use({ userPreferences: { language: FRENCH_FRANCE } }); + +test('should display default translation (en-US)', async ({ gotoAppPage }) => { + const configPage = await gotoAppPage({ pluginId: pluginJson.id, path: ROUTES.Config }); + + await expect(configPage.ctx.page.getByText('Is this translated')).toBeVisible(); +}); diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/tests/translations/swedish.spec.ts b/e2e-playwright/test-plugins/grafana-extensionstest-app/tests/translations/swedish.spec.ts new file mode 100644 index 00000000000..404f5053948 --- /dev/null +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/tests/translations/swedish.spec.ts @@ -0,0 +1,12 @@ +import { SWEDISH_SWEDEN } from '@grafana/i18n'; +import { expect, test } from '@grafana/plugin-e2e'; +import pluginJson from '../../plugin.json'; +import { ROUTES } from '../../constants'; + +test.use({ userPreferences: { language: SWEDISH_SWEDEN } }); + +test('should display correct translation', async ({ gotoAppPage }) => { + const configPage = await gotoAppPage({ pluginId: pluginJson.id, path: ROUTES.Config }); + + await expect(configPage.ctx.page.getByText('Det här är översatt')).toBeVisible(); +}); diff --git a/e2e-playwright/test-plugins/grafana-extensionstest-app/webpack.config.ts b/e2e-playwright/test-plugins/grafana-extensionstest-app/webpack.config.ts index 564555396a5..ceff913dbba 100644 --- a/e2e-playwright/test-plugins/grafana-extensionstest-app/webpack.config.ts +++ b/e2e-playwright/test-plugins/grafana-extensionstest-app/webpack.config.ts @@ -34,6 +34,7 @@ const config = async (env: Env): Promise => { ], }), ], + externals: [...(baseConfig.externals as any), 'i18next'], }; return mergeWithCustomize({ diff --git a/e2e-playwright/test-plugins/grafana-test-datasource/components/ConfigEditor.tsx b/e2e-playwright/test-plugins/grafana-test-datasource/components/ConfigEditor.tsx index 2c46992a5d9..06101e6e050 100644 --- a/e2e-playwright/test-plugins/grafana-test-datasource/components/ConfigEditor.tsx +++ b/e2e-playwright/test-plugins/grafana-test-datasource/components/ConfigEditor.tsx @@ -1,6 +1,7 @@ import { ChangeEvent } from 'react'; import { Checkbox, InlineField, InlineSwitch, Input, SecretInput, Select } from '@grafana/ui'; import { DataSourcePluginOptionsEditorProps, SelectableValue, toOption } from '@grafana/data'; +import { t } from '@grafana/i18n'; import { MyDataSourceOptions, MySecureJsonData } from '../types'; interface Props extends DataSourcePluginOptionsEditorProps {} @@ -45,36 +46,46 @@ export function ConfigEditor(props: Props) { return ( <> - + ) => onJsonDataChange('path', e.target.value)} value={jsonData.path} - placeholder="Enter the path, e.g. /api/v1" + placeholder={t('config-editor.path.placeholder', 'Enter the path, e.g. /api/v1')} width={40} /> - + ) => onSecureJsonDataChange('path', e.target.value)} /> - + ) => onJsonDataChange('switchEnabled', e.target.checked)} /> - + ) => onJsonDataChange('checkboxEnabled', e.target.checked)} /> - +