Merge remote-tracking branch 'origin/main' into grafana-git-ui-sync

This commit is contained in:
Ryan McKinley committed 2025-01-16 15:54:55 +03:00
commit 28a34ddba3
59 files changed
+360 -332

No files matched your search

+6
View File
@@ -23,6 +23,12 @@ $(BRA): $(BINGO_DIR)/bra.mod
@echo "(re)installing $(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=bra.mod -o=$(GOBIN)/bra-v0.0.0-20200517080246-1e3013ecaff8 "github.com/unknwon/bra"
COG := $(GOBIN)/cog-v0.0.15
$(COG): $(BINGO_DIR)/cog.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
@echo "(re)installing $(GOBIN)/cog-v0.0.15"
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=cog.mod -o=$(GOBIN)/cog-v0.0.15 "github.com/grafana/cog/cmd/cli"
CUE := $(GOBIN)/cue-v0.5.0
$(CUE): $(BINGO_DIR)/cue.mod
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
+5
View File
@@ -0,0 +1,5 @@
module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT
go 1.23.4
require github.com/grafana/cog v0.0.15 // cmd/cli
+18 -28
View File
@@ -1,11 +1,10 @@
cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 h1:R5wwEcbEZSBmeyg91MJZTxfd7WpBo2jPof3AYjRbxwY=
cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg=
cuelang.org/go v0.11.1 h1:pV+49MX1mmvDm8Qh3Za3M786cty8VKPWzQ1Ho4gZRP0=
cuelang.org/go v0.11.1/go.mod h1:PBY6XvPUswPPJ2inpvUozP9mebDVTXaeehQikhZPBz0=
cuelang.org/go v0.11.0 h1:2af2nhipqlUHtXk2dtOP5xnMm1ObGvKqIsJUJL1sRE4=
cuelang.org/go v0.11.0/go.mod h1:PBY6XvPUswPPJ2inpvUozP9mebDVTXaeehQikhZPBz0=
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY=
github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI=
@@ -16,18 +15,16 @@ github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
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-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/codejen v0.0.4-0.20230321061741-77f656893a3d h1:hrXbGJ5jgp6yNITzs5o+zXq0V5yT3siNJ+uM8LGwWKk=
github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s=
github.com/grafana/cog v0.0.12 h1:MJfFUVzp0El3+zZCmUQ2Y8uzwvM3aa5zj7EOeeuG6VY=
github.com/grafana/cog v0.0.12/go.mod h1:HwJbc60fZ+viayROClLGdDwO5w/JjBOpO9wjGnAfMLc=
github.com/grafana/cog v0.0.14 h1:sBK89oSu9BK4S9l3G9ewVJnGYnNQJTHFBC/01DZDRZs=
github.com/grafana/cog v0.0.14/go.mod h1:HwJbc60fZ+viayROClLGdDwO5w/JjBOpO9wjGnAfMLc=
github.com/grafana/cog v0.0.15 h1:e2pMY+Hf2nS22HcKJuguEzl0BVmV9DSINwCfWt+dFZQ=
github.com/grafana/cog v0.0.15/go.mod h1:jrS9indvWuDs60RHEZpLaAkmZdgyoLKMOEUT0jiB1t0=
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=
@@ -35,18 +32,12 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
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/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/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
@@ -61,36 +52,35 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
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/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d h1:HWfigq7lB31IeJL8iy7jkUmU/PG1Sr8jVGhS749dbUA=
github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/yalue/merged_fs v1.3.0 h1:qCeh9tMPNy/i8cwDsQTJ5bLr6IRxbs6meakNE5O+wyY=
github.com/yalue/merged_fs v1.3.0/go.mod h1:WqqchfVYQyclV2tnR7wtRhBddzBvLVR83Cjw9BKQw0M=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+2
View File
@@ -10,6 +10,8 @@ fi
BRA="${GOBIN}/bra-v0.0.0-20200517080246-1e3013ecaff8"
COG="${GOBIN}/cog-v0.0.15"
CUE="${GOBIN}/cue-v0.5.0"
DRONE="${GOBIN}/drone-v1.5.0"
+1 -1
View File
@@ -149,7 +149,7 @@ gen-cue: ## Do all CUE/Thema code generation
.PHONY: gen-cuev2
gen-cuev2: ## Do all CUE code generation
@echo "generate code from .cue files (v2)"
go generate ./kindsv2/gen.go
@$(MAKE) -C ./kindsv2 all
.PHONY: gen-feature-toggles
gen-feature-toggles:
@@ -42,7 +42,7 @@ You can create a variety of queries to visualize logs or metrics stored in Elast
For instructions on how to add a data source to Grafana, refer to the [administration documentation](ref:administration-documentation).
Only users with the organization `administrator` role can add data sources.
Administrators can also [configure the data source via YAML](#provision-the-data-source) with Grafana's provisioning system.
Administrators can also [configure the data source via YAML](ref:provisioning-data-sources) with Grafana's provisioning system.
## Configuring permissions
@@ -51,7 +51,7 @@ refs:
# Traces
Traces visualizations let you follow a request as it traverses the services in your infrastructure.
The traces visualization displays traces data in a diagram that allows you to easily interpret it.
The traces visualization displays traces data in a diagram that allows you to easily interpret it. Traces visualizations currently render one trace traversal based on the traceID used in TraceQL or using a variable.
For more information about traces and how to use them, refer to the following documentation:
-1
View File
@@ -8,7 +8,6 @@ use (
./apps/alerting/notifications
./apps/investigation
./apps/playlist
./kindsv2
./pkg/aggregator
./pkg/apimachinery
./pkg/apiserver
+8
View File
@@ -0,0 +1,8 @@
include ../.bingo/Variables.mk
.PHONY: all
all: dashboards
.PHONY: dashboards
dashboards: $(COG) ## Dashboards – Typescript
@$(COG) generate --config ./dashboard-ts.yaml
+22
View File
@@ -0,0 +1,22 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/grafana/cog/main/schemas/pipeline.json
inputs:
- cue:
entrypoint: '%__config_dir%/../packages/grafana-schema/src/schema/dashboard/v2alpha0'
metadata:
kind: core
cue_imports:
- '%__config_dir%/../packages/grafana-schema/src/common:github.com/grafana/grafana/packages/grafana-schema/src/common'
output:
directory: '%__config_dir%/../packages/grafana-schema/src/schema/dashboard/'
types: true
languages:
- typescript:
skip_runtime: true
enums_as_union_types: true
path_prefix: ""
packages_import_map:
common: '@grafana/schema'
-53
View File
@@ -1,53 +0,0 @@
//go:generate go run gen.go
package main
import (
"context"
"os"
"github.com/grafana/cog"
)
type codegenTargets struct {
modulePath string
outputPath string
cueImportsMap map[string]string
packagesImportMap map[string]string
}
func main() {
targets := []codegenTargets{
{
modulePath: "../packages/grafana-schema/src/schema/dashboard/v2alpha0/",
outputPath: "../packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.gen.ts",
cueImportsMap: map[string]string{
"github.com/grafana/grafana/packages/grafana-schema/src/common": "../packages/grafana-schema/src/common",
},
packagesImportMap: map[string]string{
"common": "@grafana/schema",
},
},
}
for _, target := range targets {
codegenPipeline := cog.TypesFromSchema().
CUEModule(
target.modulePath,
cog.CUEImports(target.cueImportsMap),
).
Typescript(cog.TypescriptConfig{
ImportsMap: target.packagesImportMap,
EnumsAsUnionTypes: true,
})
files, err := codegenPipeline.Run(context.Background())
if err != nil {
panic(err)
}
if err := os.WriteFile(target.outputPath, files[0].Data, 0644); err != nil {
panic(err)
}
}
}
-45
View File
@@ -1,45 +0,0 @@
module github.com/grafana/grafana/kindsv2
go 1.23.1
require github.com/grafana/cog v0.0.12
require (
cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 // indirect
cuelang.org/go v0.11.1 // indirect
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/proto v1.13.2 // indirect
github.com/expr-lang/expr v1.16.9 // indirect
github.com/getkin/kin-openapi v0.128.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grafana/codejen v0.0.4-0.20230321061741-77f656893a3d // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/invopop/yaml v0.3.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/yalue/merged_fs v1.3.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/tools v0.29.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
@@ -1,4 +1,4 @@
import { DashboardV2Spec } from './dashboard.gen';
import { DashboardV2Spec } from './types.gen';
export const handyTestingSchema: DashboardV2Spec = {
id: 1,
@@ -0,0 +1,4 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
export * from './types.gen';
export type * from './types.gen';
+25 -23
View File
@@ -22,8 +22,6 @@ import (
"github.com/grafana/grafana/pkg/infra/tracing"
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
"github.com/grafana/grafana/pkg/services/authz/zanzana"
zclient "github.com/grafana/grafana/pkg/services/authz/zanzana/client"
zserver "github.com/grafana/grafana/pkg/services/authz/zanzana/server"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/grpcserver"
"github.com/grafana/grafana/pkg/services/grpcserver/interceptors"
@@ -36,17 +34,17 @@ const zanzanaAudience = "zanzana"
// It will also start an embedded ZanzanaSever if mode is set to "embedded".
func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureToggles) (zanzana.Client, error) {
if !features.IsEnabledGlobally(featuremgmt.FlagZanzana) {
return zclient.NewNoop(), nil
return zanzana.NewNoopClient(), nil
}
logger := log.New("zanzana")
var client zanzana.Client
switch cfg.Zanzana.Mode {
switch cfg.ZanzanaClient.Mode {
case setting.ZanzanaModeClient:
tokenClient, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{
Token: cfg.Zanzana.Token,
TokenExchangeURL: cfg.Zanzana.TokenExchangeURL,
Token: cfg.ZanzanaClient.Token,
TokenExchangeURL: cfg.ZanzanaClient.TokenExchangeURL,
})
if err != nil {
return nil, fmt.Errorf("failed to initialize token exchange client: %w", err)
@@ -69,12 +67,12 @@ func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureTogg
grpc.WithPerRPCCredentials(tokenAuthCred),
}
conn, err := grpc.NewClient(cfg.Zanzana.Addr, dialOptions...)
conn, err := grpc.NewClient(cfg.ZanzanaClient.Addr, dialOptions...)
if err != nil {
return nil, fmt.Errorf("failed to create zanzana client to remote server: %w", err)
}
client, err = zclient.NewClient(context.Background(), conn, cfg)
client, err = zanzana.NewClient(conn)
if err != nil {
return nil, fmt.Errorf("failed to initialize zanzana client: %w", err)
}
@@ -84,12 +82,12 @@ func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureTogg
return nil, fmt.Errorf("failed to start zanzana: %w", err)
}
openfga, err := zserver.NewOpenFGA(&cfg.Zanzana, store, logger)
openfga, err := zanzana.NewOpenFGAServer(cfg.ZanzanaServer, store, logger)
if err != nil {
return nil, fmt.Errorf("failed to start zanzana: %w", err)
}
srv, err := zserver.NewAuthzServer(cfg, openfga)
srv, err := zanzana.NewServer(cfg.ZanzanaServer, openfga, logger)
if err != nil {
return nil, fmt.Errorf("failed to start zanzana: %w", err)
}
@@ -109,13 +107,13 @@ func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureTogg
authzv1.RegisterAuthzServiceServer(channel, srv)
authzextv1.RegisterAuthzExtentionServiceServer(channel, srv)
client, err = zclient.NewClient(context.Background(), channel, cfg)
client, err = zanzana.NewClient(channel)
if err != nil {
return nil, fmt.Errorf("failed to initialize zanzana client: %w", err)
}
default:
return nil, fmt.Errorf("unsupported zanzana mode: %s", cfg.Zanzana.Mode)
return nil, fmt.Errorf("unsupported zanzana mode: %s", cfg.ZanzanaClient.Mode)
}
return client, nil
@@ -156,12 +154,12 @@ func (z *Zanzana) start(ctx context.Context) error {
return fmt.Errorf("failed to initilize zanana store: %w", err)
}
openfga, err := zserver.NewOpenFGA(&z.cfg.Zanzana, store, z.logger)
openfga, err := zanzana.NewOpenFGAServer(z.cfg.ZanzanaServer, store, z.logger)
if err != nil {
return fmt.Errorf("failed to start zanzana: %w", err)
}
srv, err := zserver.NewAuthzServer(z.cfg, openfga)
srv, err := zanzana.NewServer(z.cfg.ZanzanaServer, openfga, z.logger)
if err != nil {
return fmt.Errorf("failed to start zanzana: %w", err)
}
@@ -183,7 +181,7 @@ func (z *Zanzana) start(ctx context.Context) error {
AllowedAudiences: []string{zanzanaAudience},
},
authnlib.NewKeyRetriever(authnlib.KeyRetrieverConfig{
SigningKeysURL: z.cfg.Zanzana.SigningKeysURL,
SigningKeysURL: z.cfg.ZanzanaServer.SigningKeysURL,
}),
),
)
@@ -218,14 +216,18 @@ func (z *Zanzana) start(ctx context.Context) error {
}
func (z *Zanzana) running(ctx context.Context) error {
if z.cfg.Env == setting.Dev && z.cfg.Zanzana.ListenHTTP {
go func() {
z.logger.Info("Starting OpenFGA HTTP server")
err := zserver.StartOpenFGAHttpSever(z.cfg, z.handle, z.logger)
if err != nil {
z.logger.Error("failed to start OpenFGA HTTP server", "error", err)
}
}()
if z.cfg.Env == setting.Dev && z.cfg.ZanzanaServer.OpenFGAHttpAddr != "" {
srv, err := zanzana.NewOpenFGAHttpServer(z.cfg.ZanzanaServer, z.handle)
if err != nil {
z.logger.Error("failed to create OpenFGA HTTP server", "error", err)
} else {
go func() {
z.logger.Info("Starting OpenFGA HTTP server")
if err := srv.ListenAndServe(); err != nil {
z.logger.Error("failed to start OpenFGA HTTP server", "error", err)
}
}()
}
}
// Run is blocking so we can just run it here
+5
View File
@@ -4,6 +4,7 @@ import (
"context"
"github.com/grafana/authlib/authz"
"google.golang.org/grpc"
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
"github.com/grafana/grafana/pkg/services/authz/zanzana/client"
@@ -17,6 +18,10 @@ type Client interface {
BatchCheck(ctx context.Context, req *authzextv1.BatchCheckRequest) (*authzextv1.BatchCheckResponse, error)
}
func NewClient(cc grpc.ClientConnInterface) (*client.Client, error) {
return client.New(cc)
}
func NewNoopClient() *client.NoopClient {
return client.NewNoop()
}
+2 -41
View File
@@ -2,7 +2,6 @@ package client
import (
"context"
"fmt"
"github.com/grafana/authlib/authz"
authzv1 "github.com/grafana/authlib/authz/proto/v1"
@@ -13,66 +12,28 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/infra/log"
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
"github.com/grafana/grafana/pkg/setting"
)
var _ authz.AccessClient = (*Client)(nil)
var tracer = otel.Tracer("github.com/grafana/grafana/pkg/services/authz/zanzana/client")
type ClientOption func(c *Client)
func WithTenantID(tenantID string) ClientOption {
return func(c *Client) {
c.tenantID = tenantID
}
}
func WithLogger(logger log.Logger) ClientOption {
return func(c *Client) {
c.logger = logger
}
}
type Client struct {
logger log.Logger
authz authzv1.AuthzServiceClient
authzext authzextv1.AuthzExtentionServiceClient
tenantID string
}
func NewClient(ctx context.Context, cc grpc.ClientConnInterface, cfg *setting.Cfg) (*Client, error) {
stackID := cfg.StackID
if stackID == "" {
stackID = "default"
}
return New(
ctx,
cc,
WithTenantID(fmt.Sprintf("stacks-%s", stackID)),
WithLogger(log.New("zanzana-client")),
)
}
func New(ctx context.Context, cc grpc.ClientConnInterface, opts ...ClientOption) (*Client, error) {
func New(cc grpc.ClientConnInterface) (*Client, error) {
c := &Client{
authz: authzv1.NewAuthzServiceClient(cc),
authzext: authzextv1.NewAuthzExtentionServiceClient(cc),
}
for _, o := range opts {
o(c)
}
if c.logger == nil {
c.logger = log.NewNopLogger()
logger: log.New("zanzana-client"),
}
return c, nil
}
// Check implements authz.AccessClient.
func (c *Client) Check(ctx context.Context, id claims.AuthInfo, req authz.CheckRequest) (authz.CheckResponse, error) {
ctx, span := tracer.Start(ctx, "authz.zanzana.client.Check")
defer span.End()
+25
View File
@@ -1 +1,26 @@
package zanzana
import (
"net/http"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
openfgaserver "github.com/openfga/openfga/pkg/server"
openfgastorage "github.com/openfga/openfga/pkg/storage"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/authz/zanzana/server"
"github.com/grafana/grafana/pkg/services/grpcserver"
"github.com/grafana/grafana/pkg/setting"
)
func NewServer(cfg setting.ZanzanaServerSettings, openfga openfgav1.OpenFGAServiceServer, logger log.Logger) (*server.Server, error) {
return server.NewServer(cfg, openfga, logger)
}
func NewOpenFGAServer(cfg setting.ZanzanaServerSettings, store openfgastorage.OpenFGADatastore, logger log.Logger) (*openfgaserver.Server, error) {
return server.NewOpenFGAServer(cfg, store, logger)
}
func NewOpenFGAHttpServer(cfg setting.ZanzanaServerSettings, srv grpcserver.Provider) (*http.Server, error) {
return server.NewOpenFGAHttpServer(cfg, srv)
}
@@ -14,7 +14,6 @@ import (
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/rs/cors"
"go.uber.org/zap/zapcore"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
healthv1pb "google.golang.org/grpc/health/grpc_health_v1"
@@ -27,7 +26,7 @@ import (
zlogger "github.com/grafana/grafana/pkg/services/authz/zanzana/logger"
)
func NewOpenFGA(cfg *setting.ZanzanaSettings, store storage.OpenFGADatastore, logger log.Logger) (*server.Server, error) {
func NewOpenFGAServer(cfg setting.ZanzanaServerSettings, store storage.OpenFGADatastore, logger log.Logger) (*server.Server, error) {
opts := []server.OpenFGAServiceV1Option{
server.WithDatastore(store),
server.WithLogger(zlogger.New(logger)),
@@ -48,8 +47,7 @@ func NewOpenFGA(cfg *setting.ZanzanaSettings, store storage.OpenFGADatastore, lo
return srv, nil
}
// StartOpenFGAHttpSever starts HTTP server which allows to use fga cli.
func StartOpenFGAHttpSever(cfg *setting.Cfg, srv grpcserver.Provider, logger log.Logger) error {
func NewOpenFGAHttpServer(cfg setting.ZanzanaServerSettings, srv grpcserver.Provider) (*http.Server, error) {
dialOpts := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
}
@@ -66,12 +64,12 @@ func StartOpenFGAHttpSever(cfg *setting.Cfg, srv grpcserver.Provider, logger log
retries++
}
if addr == "" {
return fmt.Errorf("failed to start HTTP server: GRPC server unavailable")
return nil, fmt.Errorf("failed to create HTTP server: GRPC server unavailable")
}
conn, err := grpc.NewClient(addr, dialOpts...)
if err != nil {
return fmt.Errorf("unable to dial GRPC: %w", err)
return nil, fmt.Errorf("unable to dial GRPC: %w", err)
}
muxOpts := []runtime.ServeMuxOption{
@@ -91,11 +89,11 @@ func StartOpenFGAHttpSever(cfg *setting.Cfg, srv grpcserver.Provider, logger log
}
mux := runtime.NewServeMux(muxOpts...)
if err := openfgav1.RegisterOpenFGAServiceHandler(context.TODO(), mux, conn); err != nil {
return fmt.Errorf("failed to register gateway handler: %w", err)
return nil, fmt.Errorf("failed to register gateway handler: %w", err)
}
httpServer := &http.Server{
Addr: cfg.Zanzana.HttpAddr,
return &http.Server{
Addr: cfg.OpenFGAHttpAddr,
Handler: cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowCredentials: true,
@@ -104,13 +102,5 @@ func StartOpenFGAHttpSever(cfg *setting.Cfg, srv grpcserver.Provider, logger log
http.MethodHead, http.MethodPatch, http.MethodDelete, http.MethodPut},
}).Handler(mux),
ReadHeaderTimeout: 30 * time.Second,
}
go func() {
err = httpServer.ListenAndServe()
if err != nil {
logger.Error("failed to start http server", zapcore.Field{Key: "err", Type: zapcore.ErrorType, Interface: err})
}
}()
logger.Info(fmt.Sprintf("OpenFGA HTTP server listening on '%s'...", httpServer.Addr))
return nil
}, nil
}
+5 -32
View File
@@ -9,7 +9,6 @@ import (
"github.com/fullstorydev/grpchan/inprocgrpc"
authzv1 "github.com/grafana/authlib/authz/proto/v1"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
"github.com/openfga/language/pkg/go/transformer"
"go.opentelemetry.io/otel"
dashboardalpha1 "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1"
@@ -34,9 +33,8 @@ type Server struct {
openfga openfgav1.OpenFGAServiceServer
openfgaClient openfgav1.OpenFGAServiceClient
cfg setting.ZanzanaSettings
cfg setting.ZanzanaServerSettings
logger log.Logger
modules []transformer.ModuleFile
stores map[string]storeInfo
storesMU *sync.Mutex
cache *localcache.CacheService
@@ -47,25 +45,7 @@ type storeInfo struct {
ModelID string
}
type ServerOption func(s *Server)
func WithLogger(logger log.Logger) ServerOption {
return func(s *Server) {
s.logger = logger
}
}
func WithSchema(modules []transformer.ModuleFile) ServerOption {
return func(s *Server) {
s.modules = modules
}
}
func NewAuthzServer(cfg *setting.Cfg, openfga openfgav1.OpenFGAServiceServer) (*Server, error) {
return NewAuthz(cfg, openfga)
}
func NewAuthz(cfg *setting.Cfg, openfga openfgav1.OpenFGAServiceServer, opts ...ServerOption) (*Server, error) {
func NewServer(cfg setting.ZanzanaServerSettings, openfga openfgav1.OpenFGAServiceServer, logger log.Logger) (*Server, error) {
channel := &inprocgrpc.Channel{}
openfgav1.RegisterOpenFGAServiceServer(channel, openfga)
openFGAClient := openfgav1.NewOpenFGAServiceClient(channel)
@@ -75,16 +55,9 @@ func NewAuthz(cfg *setting.Cfg, openfga openfgav1.OpenFGAServiceServer, opts ...
openfgaClient: openFGAClient,
storesMU: &sync.Mutex{},
stores: make(map[string]storeInfo),
cfg: cfg.Zanzana,
cache: localcache.New(cfg.Zanzana.CheckQueryCacheTTL, cacheCleanInterval),
}
for _, o := range opts {
o(s)
}
if s.logger == nil {
s.logger = log.New("authz-server")
cfg: cfg,
cache: localcache.New(cfg.CheckQueryCacheTTL, cacheCleanInterval),
logger: logger,
}
return s, nil
@@ -71,10 +71,10 @@ func setup(t *testing.T, testDB db.DB, cfg *setting.Cfg) *Server {
t.Helper()
store, err := store.NewEmbeddedStore(cfg, testDB, log.NewNopLogger())
require.NoError(t, err)
openfga, err := NewOpenFGA(&cfg.Zanzana, store, log.NewNopLogger())
openfga, err := NewOpenFGAServer(cfg.ZanzanaServer, store, log.NewNopLogger())
require.NoError(t, err)
srv, err := NewAuthz(cfg, openfga)
srv, err := NewServer(cfg.ZanzanaServer, openfga, log.NewNopLogger())
require.NoError(t, err)
storeInf, err := srv.getStoreInfo(context.Background(), namespace)
+2 -1
View File
@@ -474,7 +474,8 @@ type Cfg struct {
RBAC RBACSettings
Zanzana ZanzanaSettings
ZanzanaClient ZanzanaClientSettings
ZanzanaServer ZanzanaServerSettings
// GRPC Server.
GRPCServer GRPCServerSettings
+40 -40
View File
@@ -12,20 +12,24 @@ const (
ZanzanaModeEmbedded ZanzanaMode = "embedded"
)
type ZanzanaSettings struct {
// Addr is only used when mode is set to client
Addr string
// Mode can either be embedded or client
type ZanzanaClientSettings struct {
// Mode can either be embedded or client.
Mode ZanzanaMode
// ListenHTTP enables OpenFGA http server which allows to use fga cli
ListenHTTP bool
// OpenFGA http server address which allows to connect with fga cli
HttpAddr string
// If enabled, authorization cheks will be only performed by zanzana.
// This bypasses the performance comparison with the legacy system.
ZanzanaOnlyEvaluation bool
// Number of concurrent check requests running by Grafana.
ConcurrentChecks int64
// Addr is the address of the Zanzana server.
// Only used when mode is set to client.
Addr string
// Token used to perform the exchange request.
// Only used when mode is set to client.
Token string
// URL called to perform exchange request.
// Only used when mode is set to client.
TokenExchangeURL string
}
type ZanzanaServerSettings struct {
// OpenFGA http server address which allows to connect with fga cli.
// Can only be used in dev mode.
OpenFGAHttpAddr string
// Enable cache for Check() requests
CheckQueryCache bool
// TTL for cached requests. Default is 10 seconds.
@@ -37,42 +41,38 @@ type ZanzanaSettings struct {
// Use streamed version of list objects.
// Returns full list of objects, but takes more time.
UseStreamedListObjects bool
// Token used to perform the exchange request.
Token string
// URL called to perform exchange request.
TokenExchangeURL string
// URL for signing keys
// URL for fetching signing keys.
SigningKeysURL string
}
func (cfg *Cfg) readZanzanaSettings() {
s := ZanzanaSettings{}
sec := cfg.Raw.Section("zanzana")
s.Mode = ZanzanaMode(sec.Key("mode").MustString("embedded"))
zc := ZanzanaClientSettings{}
clientSec := cfg.Raw.Section("zanzana.client")
zc.Mode = ZanzanaMode(clientSec.Key("mode").MustString("embedded"))
validModes := []ZanzanaMode{ZanzanaModeEmbedded, ZanzanaModeClient}
if !slices.Contains(validModes, s.Mode) {
cfg.Logger.Warn("Invalid zanzana mode", "expected", validModes, "got", s.Mode)
s.Mode = "embedded"
if !slices.Contains(validModes, zc.Mode) {
cfg.Logger.Warn("Invalid zanzana mode", "expected", validModes, "got", zc.Mode)
zc.Mode = "embedded"
}
s.Addr = sec.Key("address").MustString("")
s.ListenHTTP = sec.Key("listen_http").MustBool(false)
s.HttpAddr = sec.Key("http_addr").MustString("127.0.0.1:8080")
s.ConcurrentChecks = sec.Key("concurrent_checks").MustInt64(10)
s.ZanzanaOnlyEvaluation = sec.Key("zanzana_only_evaluation").MustBool(false)
s.CheckQueryCache = sec.Key("check_query_cache").MustBool(true)
s.CheckQueryCacheTTL = sec.Key("check_query_cache_ttl").MustDuration(10 * time.Second)
s.ListObjectsDeadline = sec.Key("list_objects_deadline").MustDuration(3 * time.Second)
s.ListObjectsMaxResults = uint32(sec.Key("list_objects_max_results").MustUint(1000))
s.UseStreamedListObjects = sec.Key("use_streamed_list_objects").MustBool(false)
zc.Token = clientSec.Key("token").MustString("")
zc.TokenExchangeURL = clientSec.Key("token_exchange_url").MustString("")
zc.Addr = clientSec.Key("address").MustString("")
s.Token = sec.Key("token").MustString("")
s.TokenExchangeURL = sec.Key("token_exchange_url").MustString("")
s.SigningKeysURL = sec.Key("signing_keys_url").MustString("")
cfg.ZanzanaClient = zc
cfg.Zanzana = s
zs := ZanzanaServerSettings{}
serverSec := cfg.Raw.Section("zanzana.server")
zs.OpenFGAHttpAddr = serverSec.Key("http_addr").MustString("127.0.0.1:8080")
zs.CheckQueryCache = serverSec.Key("check_query_cache").MustBool(true)
zs.CheckQueryCacheTTL = serverSec.Key("check_query_cache_ttl").MustDuration(10 * time.Second)
zs.ListObjectsDeadline = serverSec.Key("list_objects_deadline").MustDuration(3 * time.Second)
zs.ListObjectsMaxResults = uint32(serverSec.Key("list_objects_max_results").MustUint(1000))
zs.UseStreamedListObjects = serverSec.Key("use_streamed_list_objects").MustBool(false)
zs.SigningKeysURL = serverSec.Key("signing_keys_url").MustString("")
cfg.ZanzanaServer = zs
}
+36
View File
@@ -1,3 +1,5 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
@@ -80,6 +82,8 @@ github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vS
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30 h1:hXVi7QKuCQ0E8Yujfu9b0f0RnzZ72efpWvPnZgnJPrE=
github.com/apache/arrow-go/v18 v18.0.1-0.20241212180703-82be143d7c30/go.mod h1:RNuWDIiGjq5nndL2PyQrndUy9nMLwheA3uWaAV7fe4U=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@@ -354,6 +358,8 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/flatbuffers v24.3.25+incompatible h1:CX395cjN9Kke9mmalRoL3d81AtFUxJM+yDthflgJGkI=
github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
@@ -430,6 +436,8 @@ github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKt
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grafana/sqlds/v4 v4.1.3 h1:+Hy5Yz+tSbD5N3yuLM0VKTsWlVaCzM1S1m1QEBZL7fE=
github.com/grafana/sqlds/v4 v4.1.3/go.mod h1:Lx8IR939lIrCBpCKthv7AXs7E7bmNWPgt0gene/idT8=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0 h1:kQ0NI7W1B3HwiN5gAYtY+XFItDPbLBwYRxAqbFTyDes=
@@ -467,6 +475,8 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
@@ -561,6 +571,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattetti/filebuffer v1.0.1 h1:gG7pyfnSIZCxdoKq+cPa8T0hhYtD9NxCdI4D7PTjRLM=
@@ -631,6 +643,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA=
github.com/natefinch/wrap v0.2.0 h1:IXzc/pw5KqxJv55gV0lSOcKHYuEZPGbQrOOXr/bamRk=
github.com/natefinch/wrap v0.2.0/go.mod h1:6gMHlAl12DwYEfKP3TkuykYUfLSEAvHw67itm4/KAS8=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@@ -649,8 +663,12 @@ github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQ
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/openfga/api/proto v0.0.0-20240906203051-102620ef2a66 h1:pAYrdyIKxPsMs/nRcTEnS9za2io11g7Rt7ng7HK82hk=
github.com/openfga/api/proto v0.0.0-20240906203051-102620ef2a66/go.mod h1:gil5LBD8tSdFQbUkCQdnXsoeU9kDJdJgbGdHkgJfcd0=
github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20240926131254-992b301a003f h1:ZMZ7ntMnaHIPZxvVQv/aqC4ctzLqH+9Fqn4uw35kQpk=
github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20240926131254-992b301a003f/go.mod h1:ll/hN6kS4EE6B/7J/PbZqac9Nuv7ZHpI+Jfh36JLrbs=
github.com/openfga/openfga v1.6.2 h1:tHBAgiCPomCZb3IH0CFqOpTDVDA/Xb8kG87oX4JCbXc=
github.com/openfga/openfga v1.6.2/go.mod h1:jzbEpheazf6MFjtanQt1rpxexSRzfa9057F7JlkMv2I=
github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w=
github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
@@ -659,6 +677,8 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@@ -720,6 +740,10 @@ github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
@@ -740,12 +764,20 @@ github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PX
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -765,6 +797,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA=
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0=
github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
@@ -845,6 +879,8 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
@@ -4,10 +4,7 @@ import { render, screen, userEvent, waitFor } from 'test/test-utils';
import { config } from '@grafana/runtime';
import { defaultDashboard as defaultDashboardData } from '@grafana/schema';
import {
DashboardV2Spec,
defaultDashboardV2Spec,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec, defaultDashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { backendSrv } from 'app/core/services/backend_srv';
import { DashboardWithAccessInfo } from 'app/features/dashboard/api/types';
import { DashboardSearchItemType } from 'app/features/search/types';
@@ -1,10 +1,7 @@
import { advanceBy } from 'jest-date-mock';
import { BackendSrv, setBackendSrv } from '@grafana/runtime';
import {
DashboardV2Spec,
defaultDashboardV2Spec,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec, defaultDashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import store from 'app/core/store';
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
import { DashboardWithAccessInfo } from 'app/features/dashboard/api/types';
@@ -2,7 +2,7 @@ import { isEqual } from 'lodash';
import { locationUtil, UrlQueryMap } from '@grafana/data';
import { config, getBackendSrv, isFetchError, locationService } from '@grafana/runtime';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { StateManagerBase } from 'app/core/services/StateManagerBase';
import { getMessageFromError } from 'app/core/utils/errors';
import { startMeasure, stopMeasure } from 'app/core/utils/metrics';
@@ -7,7 +7,7 @@ import {
AdhocVariableSpec,
DashboardV2Spec,
VariableKind,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { jsonDiff } from '../settings/version-history/utils';
@@ -3,7 +3,7 @@ import * as React from 'react';
import { selectors } from '@grafana/e2e-selectors';
import { config, isFetchError } from '@grafana/runtime';
import { Dashboard } from '@grafana/schema';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { Alert, Box, Button, Stack } from '@grafana/ui';
import { t, Trans } from 'app/core/internationalization';
@@ -25,7 +25,7 @@ import {
VizPanel,
} from '@grafana/scenes';
import { Dashboard, DashboardLink, LibraryPanel } from '@grafana/schema';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import appEvents from 'app/core/app_events';
import { ScrollRefElement } from 'app/core/components/NativeScrollbar';
import { LS_PANEL_COPY_KEY } from 'app/core/constants';
@@ -12,7 +12,7 @@ import {
defaultDashboardV2Spec,
defaultPanelSpec,
defaultTimeSettingsSpec,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { AnnoKeyDashboardSnapshotOriginalUrl } from 'app/features/apiserver/types';
import { DASHBOARD_SCHEMA_VERSION } from 'app/features/dashboard/state/DashboardMigrator';
@@ -1,6 +1,6 @@
import { config } from '@grafana/runtime';
import { Dashboard } from '@grafana/schema';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { AnnoKeyDashboardSnapshotOriginalUrl } from 'app/features/apiserver/types';
import { DashboardWithAccessInfo } from 'app/features/dashboard/api/types';
import { SaveDashboardAsOptions } from 'app/features/dashboard/components/SaveDashboard/types';
@@ -8,7 +8,7 @@ import {
defaultGroupByVariableSpec,
defaultTimeSettingsSpec,
GroupByVariableKind,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { AnnoKeyDashboardIsNew, AnnoKeyFolder } from 'app/features/apiserver/types';
import { DashboardWithAccessInfo } from 'app/features/dashboard/api/types';
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
@@ -18,7 +18,7 @@ import {
GroupByVariableKind,
defaultVariableHide,
VariableOption,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { getIntervalsQueryFromNewIntervalModel } from '../utils/utils';
@@ -24,7 +24,7 @@ import {
IntervalVariableKind,
QueryVariableKind,
TextVariableKind,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { handyTestingSchema } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/examples';
import { AnnoKeyDashboardIsNew } from 'app/features/apiserver/types';
import { DashboardWithAccessInfo } from 'app/features/dashboard/api/types';
@@ -50,7 +50,7 @@ import {
PanelQueryKind,
QueryVariableKind,
TextVariableKind,
} from '@grafana/schema/src/schema/dashboard/v2alpha0/dashboard.gen';
} from '@grafana/schema/src/schema/dashboard/v2alpha0';
import { contextSrv } from 'app/core/core';
import {
AnnoKeyCreatedBy,
@@ -37,7 +37,7 @@ import {
AnnotationQueryKind,
DataLink,
RepeatOptions,
} from '../../../../../packages/grafana-schema/src/schema/dashboard/v2alpha0/dashboard.gen';
} from '../../../../../packages/grafana-schema/src/schema/dashboard/v2alpha0';
import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet';
import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene';
import { PanelTimeRange } from '../scene/PanelTimeRange';
@@ -16,7 +16,7 @@ import {
FieldConfigSource,
SpecialValueMatch,
ThresholdsMode,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
export function transformVariableRefreshToEnumV1(refresh?: VariableRefresh): VariableRefreshV1 {
switch (refresh) {
@@ -3,7 +3,7 @@ import {
defaultVariableSort,
defaultVariableRefresh,
defaultDashboardCursorSync,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import {
transformCursorSynctoEnum,
@@ -15,7 +15,7 @@ import {
VariableHide,
VariableRefresh,
VariableSort,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
export function transformCursorSynctoEnum(cursorSync?: DashboardCursorSyncV1): DashboardCursorSync {
switch (cursorSync) {
@@ -1,5 +1,6 @@
import { ConfirmModal } from '@grafana/ui';
import { useAppNotification } from 'app/core/copy/appNotification';
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
import { DecoratedRevisionModel } from '../VersionsEditView';
@@ -17,6 +18,7 @@ export const RevertDashboardModal = ({ hideModal, onRestore, version }: RevertDa
if (success) {
notifyApp.success('Dashboard restored', `Restored from version ${version.version}`);
DashboardInteractions.versionRestoreClicked({ version: version.version, confirm: true });
} else {
notifyApp.error('Dashboard restore failed', `Failed to restore from version ${version.version}`);
}
@@ -0,0 +1,30 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { DashboardInteractions } from '../../utils/interactions';
import { VersionsHistoryButtons } from './VersionHistoryButtons';
jest.mock('../../utils/interactions', () => ({
DashboardInteractions: {
showMoreVersionsClicked: jest.fn(),
},
}));
describe('VersionHistoryButtons', () => {
it('triggers a user event when the show more versions is clicked', async () => {
render(
<VersionsHistoryButtons
getVersions={jest.fn()}
canCompare={true}
hasMore={true}
getDiff={jest.fn()}
isLastPage={false}
/>
);
const showMoreButton = screen.getByText('Show more versions');
fireEvent.click(showMoreButton);
expect(DashboardInteractions.showMoreVersionsClicked).toHaveBeenCalledWith();
});
});
@@ -1,4 +1,5 @@
import { Tooltip, Button, Stack } from '@grafana/ui';
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
type VersionsButtonsType = {
hasMore: boolean;
@@ -16,7 +17,15 @@ export const VersionsHistoryButtons = ({
}: VersionsButtonsType) => (
<Stack>
{hasMore && (
<Button type="button" onClick={() => getVersions(true)} variant="secondary" disabled={isLastPage}>
<Button
type="button"
onClick={() => {
getVersions(true);
DashboardInteractions.showMoreVersionsClicked();
}}
variant="secondary"
disabled={isLastPage}
>
Show more versions
</Button>
)}
@@ -0,0 +1,57 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { DashboardInteractions } from '../../utils/interactions';
import { DecoratedRevisionModel } from '../VersionsEditView';
import { VersionHistoryTable } from './VersionHistoryTable';
jest.mock('../../utils/interactions', () => ({
DashboardInteractions: {
versionRestoreClicked: jest.fn(),
showMoreVersionsClicked: jest.fn(),
},
}));
const mockVersions: DecoratedRevisionModel[] = [
{
id: 1,
version: 1,
createdDateString: '2023-01-01',
createdBy: 'User1',
message: 'Initial version',
checked: false,
uid: '0',
parentVersion: 0,
created: new Date(),
data: { schemaVersion: 1, uid: '0', title: 'Dashboard', panels: [] },
ageString: '1 day ago',
},
{
id: 2,
version: 2,
createdDateString: '2023-02-01',
createdBy: 'User2',
message: 'Second version',
checked: false,
uid: '0',
parentVersion: 0,
created: new Date(),
data: { schemaVersion: 1, uid: '0', title: 'Dashboard', panels: [] },
ageString: '10 days ago',
},
];
describe('VersionHistoryTable', () => {
it('triggers a user event when the restore button is clicked', async () => {
render(<VersionHistoryTable versions={mockVersions} canCompare={true} onCheck={jest.fn()} onRestore={jest.fn()} />);
const restoreButtons = screen.getAllByText('Restore');
fireEvent.click(restoreButtons[0]);
expect(DashboardInteractions.versionRestoreClicked).toHaveBeenCalledWith({
version: mockVersions[1].version,
index: 1,
confirm: false,
});
});
});
@@ -3,6 +3,7 @@ import * as React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { Checkbox, Button, Tag, ModalsController, useStyles2 } from '@grafana/ui';
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
import { DecoratedRevisionModel } from '../VersionsEditView';
@@ -65,6 +66,11 @@ export const VersionHistoryTable = ({ versions, canCompare, onCheck, onRestore }
hideModal,
onRestore,
});
DashboardInteractions.versionRestoreClicked({
version: version.version,
index: idx,
confirm: false,
});
}}
>
Restore
@@ -116,6 +116,14 @@ export const DashboardInteractions = {
isScenesContextSet = false;
};
},
// Dashboards versions interactions
versionRestoreClicked: (properties: { version: number; index?: number; confirm: boolean }) => {
reportDashboardInteraction('version_restore_clicked', properties);
},
showMoreVersionsClicked: () => {
reportDashboardInteraction('show_more_versions_clicked');
},
};
const reportDashboardInteraction: typeof reportInteraction = (name, properties) => {
@@ -10,7 +10,7 @@ import {
SceneVariableState,
VizPanel,
} from '@grafana/scenes';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { DashboardScene } from '../scene/DashboardScene';
import { VizPanelLinks } from '../scene/PanelLinks';
@@ -1,6 +1,6 @@
import { config } from '@grafana/runtime';
import { CustomVariable, GroupByVariable } from '@grafana/scenes';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { handyTestingSchema } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/examples';
import { DashboardWithAccessInfo } from 'app/features/dashboard/api/types';
@@ -1,5 +1,5 @@
import { DataQuery } from '@grafana/schema';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import {
AnnoKeyCreatedBy,
AnnoKeyDashboardGnetId,
@@ -11,7 +11,7 @@ import {
PanelQueryKind,
QueryVariableKind,
TransformationKind,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { DataTransformerConfig } from '@grafana/schema/src/raw/dashboard/x/dashboard_types.gen';
import {
AnnoKeyCreatedBy,
@@ -1,4 +1,4 @@
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { DashboardDTO } from 'app/types';
import { LegacyDashboardAPI } from './legacy';
+1 -1
View File
@@ -1,5 +1,5 @@
import { config, locationService } from '@grafana/runtime';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { DashboardDataDTO, DashboardDTO } from 'app/types';
import { DashboardWithAccessInfo } from './types';
+1 -4
View File
@@ -1,7 +1,4 @@
import {
DashboardV2Spec,
defaultDashboardV2Spec,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec, defaultDashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { backendSrv } from 'app/core/services/backend_srv';
import { AnnoKeyFolder, AnnoKeyFolderId, AnnoKeyFolderTitle, AnnoKeyFolderUrl } from 'app/features/apiserver/types';
+1 -1
View File
@@ -1,5 +1,5 @@
import { UrlQueryMap } from '@grafana/data';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { backendSrv } from 'app/core/services/backend_srv';
import { ScopedResourceClient } from 'app/features/apiserver/client';
import {
@@ -1,5 +1,5 @@
import { Dashboard } from '@grafana/schema';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { ObjectMeta } from 'app/features/apiserver/types';
import { CloneOptions, DashboardModel } from 'app/features/dashboard/state/DashboardModel';
import { Diffs } from 'app/features/dashboard-scene/settings/version-history/utils';
@@ -4,10 +4,7 @@ import moment from 'moment'; // eslint-disable-line no-restricted-imports
import { AppEvents, dateMath, UrlQueryMap, UrlQueryValue } from '@grafana/data';
import { getBackendSrv, isFetchError, locationService } from '@grafana/runtime';
import {
DashboardV2Spec,
defaultDashboardV2Spec,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec, defaultDashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { backendSrv } from 'app/core/services/backend_srv';
import impressionSrv from 'app/core/services/impression_srv';
import { getMessageFromError } from 'app/core/utils/errors';
@@ -1,5 +1,5 @@
import { VariableModel } from '@grafana/schema/dist/esm/index';
import { VariableKind } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { VariableKind } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
@@ -1,7 +1,4 @@
import {
DashboardV2Spec,
defaultDashboardV2Spec,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { DashboardV2Spec, defaultDashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0';
import { AnnoKeyFolder, AnnoKeyFolderTitle } from 'app/features/apiserver/types';
import { setDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
import { DashboardWithAccessInfo } from 'app/features/dashboard/api/types';