diff --git a/.bra.toml b/.bra.toml index 6572f9a6afb..5af8e05ebcc 100644 --- a/.bra.toml +++ b/.bra.toml @@ -17,7 +17,7 @@ watch_exts = [".go", ".ini", ".toml", ".template.html"] ignore_files = [".*_gen.go"] build_delay = 1500 cmds = [ - ["make", "GO_BUILD_DEV=1", "build-go-fast"], + ["make", "GO_BUILD_DEV=1", "build-go"], ["make", "gen-jsonnet"], ["./bin/grafana", "server", "-profile", "-profile-addr=127.0.0.1", "-profile-port=6000", "-profile-block-rate=1", "-profile-mutex-rate=5", "-packaging=dev", "cfg:app_mode=development"] ] diff --git a/.citools/src/air/go.mod b/.citools/src/air/go.mod index 6fd4736b93d..8619eb0641e 100644 --- a/.citools/src/air/go.mod +++ b/.citools/src/air/go.mod @@ -1,6 +1,6 @@ module air -go 1.24.4 +go 1.25.3 tool github.com/air-verse/air @@ -21,7 +21,8 @@ require ( github.com/spf13/afero v1.14.0 // indirect github.com/spf13/cast v1.8.0 // indirect github.com/tdewolff/parse/v2 v2.8.1 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect + golang.org/x/net v0.45.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect google.golang.org/protobuf v1.36.6 // indirect ) diff --git a/.citools/src/air/go.sum b/.citools/src/air/go.sum index d733e3519f3..fbefab6f556 100644 --- a/.citools/src/air/go.sum +++ b/.citools/src/air/go.sum @@ -1,45 +1,190 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69 h1:+tu3HOoMXB7RXEINRVIpxJCT+KdYiI7LAEAUrOw3dIU= +github.com/BurntSushi/locker v0.0.0-20171006230638-a6e239ea1c69/go.mod h1:L1AbZdiDllfyYH5l5OkAaZtk7VkWe89bPJFmnDBNHxg= github.com/air-verse/air v1.62.0 h1:6CoXL4MAX9dc4xAzLfjMcDfbBoGmW5VjuuTV/1+bI+M= github.com/air-verse/air v1.62.0/go.mod h1:EO+jWuetL10tS9raffwg8WEV0t0KUeucRRaf9ii86dA= +github.com/alecthomas/chroma/v2 v2.17.2 h1:Rm81SCZ2mPoH+Q8ZCc/9YvzPUN/E7HgPiPJD8SLV6GI= +github.com/alecthomas/chroma/v2 v2.17.2/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= +github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c h1:651/eoCRnQ7YtSjAnSzRucrJz+3iGEFt+ysraELS81M= +github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/bep/clocks v0.5.0 h1:hhvKVGLPQWRVsBP/UB7ErrHYIO42gINVbvqxvYTPVps= +github.com/bep/clocks v0.5.0/go.mod h1:SUq3q+OOq41y2lRQqH5fsOoxN8GbxSiT6jvoVVLCVhU= +github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= +github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= +github.com/bep/gitmap v1.6.0 h1:sDuQMm9HoTL0LtlrfxjbjgAg2wHQd4nkMup2FInYzhA= +github.com/bep/gitmap v1.6.0/go.mod h1:n+3W1f/rot2hynsqEGxGMErPRgT41n9CkGuzPvz9cIw= +github.com/bep/goat v0.5.0 h1:S8jLXHCVy/EHIoCY+btKkmcxcXFd34a0Q63/0D4TKeA= +github.com/bep/goat v0.5.0/go.mod h1:Md9x7gRxiWKs85yHlVTvHQw9rg86Bm+Y4SuYE8CTH7c= github.com/bep/godartsass/v2 v2.5.0 h1:tKRvwVdyjCIr48qgtLa4gHEdtRkPF8H1OeEhJAEv7xg= github.com/bep/godartsass/v2 v2.5.0/go.mod h1:rjsi1YSXAl/UbsGL85RLDEjRKdIKUlMQHr6ChUNYOFU= github.com/bep/golibsass v1.2.0 h1:nyZUkKP/0psr8nT6GR2cnmt99xS93Ji82ZD9AgOK6VI= github.com/bep/golibsass v1.2.0/go.mod h1:DL87K8Un/+pWUS75ggYv41bliGiolxzDKWJAq3eJ1MA= +github.com/bep/goportabletext v0.1.0 h1:8dqym2So1cEqVZiBa4ZnMM1R9l/DnC1h4ONg4J5kujw= +github.com/bep/goportabletext v0.1.0/go.mod h1:6lzSTsSue75bbcyvVc0zqd1CdApuT+xkZQ6Re5DzZFg= +github.com/bep/gowebp v0.4.0 h1:QihuVnvIKbRoeBNQkN0JPMM8ClLmD6V2jMftTFwSK3Q= +github.com/bep/gowebp v0.4.0/go.mod h1:95gtYkAA8iIn1t3HkAPurRCVGV/6NhgaHJ1urz0iIwc= +github.com/bep/imagemeta v0.12.0 h1:ARf+igs5B7pf079LrqRnwzQ/wEB8Q9v4NSDRZO1/F5k= +github.com/bep/imagemeta v0.12.0/go.mod h1:23AF6O+4fUi9avjiydpKLStUNtJr5hJB4rarG18JpN8= +github.com/bep/lazycache v0.8.0 h1:lE5frnRjxaOFbkPZ1YL6nijzOPPz6zeXasJq8WpG4L8= +github.com/bep/lazycache v0.8.0/go.mod h1:BQ5WZepss7Ko91CGdWz8GQZi/fFnCcyWupv8gyTeKwk= +github.com/bep/logg v0.4.0 h1:luAo5mO4ZkhA5M1iDVDqDqnBBnlHjmtZF6VAyTp+nCQ= +github.com/bep/logg v0.4.0/go.mod h1:Ccp9yP3wbR1mm++Kpxet91hAZBEQgmWgFgnXX3GkIV0= +github.com/bep/overlayfs v0.10.0 h1:wS3eQ6bRsLX+4AAmwGjvoFSAQoeheamxofFiJ2SthSE= +github.com/bep/overlayfs v0.10.0/go.mod h1:ouu4nu6fFJaL0sPzNICzxYsBeWwrjiTdFZdK4lI3tro= +github.com/bep/tmc v0.5.1 h1:CsQnSC6MsomH64gw0cT5f+EwQDcvZz4AazKunFwTpuI= +github.com/bep/tmc v0.5.1/go.mod h1:tGYHN8fS85aJPhDLgXETVKp+PR382OvFi2+q2GkGsq0= +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/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/disintegration/gift v1.2.1 h1:Y005a1X4Z7Uc+0gLpSAsKhWi4qLtsdEcMIbbdvdZ6pc= +github.com/disintegration/gift v1.2.1/go.mod h1:Jh2i7f7Q2BM7Ezno3PhfezbR1xpUg9dUg3/RlKGr4HI= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/evanw/esbuild v0.25.3 h1:4JKyUsm/nHDhpxis4IyWXAi8GiyTwG1WdEp6OhGVE8U= +github.com/evanw/esbuild v0.25.3/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= +github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +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/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= +github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e h1:QArsSubW7eDh8APMXkByjQWvuljwPGAGQpJEFn0F0wY= +github.com/gohugoio/go-i18n/v2 v2.1.3-0.20230805085216-e63c13218d0e/go.mod h1:3Ltoo9Banwq0gOtcOwxuHG6omk+AwsQPADyw2vQYOJQ= +github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp47K9swqg= +github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec= +github.com/gohugoio/httpcache v0.7.0 h1:ukPnn04Rgvx48JIinZvZetBfHaWE7I01JR2Q2RrQ3Vs= +github.com/gohugoio/httpcache v0.7.0/go.mod h1:fMlPrdY/vVJhAriLZnrF5QpN3BNAcoBClgAyQd+lGFI= github.com/gohugoio/hugo v0.147.6 h1:rL4rnus/5qzj4+FoA+JMzsVvFJ2YZdVIH6pbuCB2P84= github.com/gohugoio/hugo v0.147.6/go.mod h1:Sb2COQPDPYG+tRSpePtzKytiuVDqkBivEhgIew1QbNo= +github.com/gohugoio/hugo-goldmark-extensions/extras v0.3.0 h1:gj49kTR5Z4Hnm0ZaQrgPVazL3DUkppw+x6XhHCmh+Wk= +github.com/gohugoio/hugo-goldmark-extensions/extras v0.3.0/go.mod h1:IMMj7xiUbLt1YNJ6m7AM4cnsX4cFnnfkleO/lBHGzUg= +github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.1 h1:nUzXfRTszLliZuN0JTKeunXTRaiFX6ksaWP0puLLYAY= +github.com/gohugoio/hugo-goldmark-extensions/passthrough v0.3.1/go.mod h1:Wy8ThAA8p2/w1DY05vEzq6EIeI2mzDjvHsu7ULBVwog= +github.com/gohugoio/locales v0.14.0 h1:Q0gpsZwfv7ATHMbcTNepFd59H7GoykzWJIxi113XGDc= +github.com/gohugoio/locales v0.14.0/go.mod h1:ip8cCAv/cnmVLzzXtiTpPwgJ4xhKZranqNqtoIu0b/4= +github.com/gohugoio/localescompressed v1.0.1 h1:KTYMi8fCWYLswFyJAeOtuk/EkXR/KPTHHNN9OS+RTxo= +github.com/gohugoio/localescompressed v1.0.1/go.mod h1:jBF6q8D7a0vaEmcWPNcAjUZLJaIVNiwvM3WlmTvooB0= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +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/hairyhenderson/go-codeowners v0.7.0 h1:s0W4wF8bdsBEjTWzwzSlsatSthWtTAF2xLgo4a4RwAo= +github.com/hairyhenderson/go-codeowners v0.7.0/go.mod h1:wUlNgQ3QjqC4z8DnM5nnCYVq/icpqXJyJOukKx5U8/Q= +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/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU= +github.com/jdkato/prose v1.2.1/go.mod h1:AiRHgVagnEx2JbQRQowVBKjG0bcs/vtkGCH1dYAL1rA= +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.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kyokomi/emoji/v2 v2.2.13 h1:GhTfQa67venUUvmleTNFnb+bi7S3aocF7ZCXU9fSO7U= +github.com/kyokomi/emoji/v2 v2.2.13/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/makeworld-the-better-one/dither/v2 v2.4.0 h1:Az/dYXiTcwcRSe59Hzw4RI1rSnAZns+1msaCXetrMFE= +github.com/makeworld-the-better-one/dither/v2 v2.4.0/go.mod h1:VBtN8DXO7SNtyGmLiGA7IsFeKrBkQPze1/iAeM95arc= +github.com/marekm4/color-extractor v1.2.1 h1:3Zb2tQsn6bITZ8MBVhc33Qn1k5/SEuZ18mrXGUqIwn0= +github.com/marekm4/color-extractor v1.2.1/go.mod h1:90VjmiHI6M8ez9eYUaXLdcKnS+BAOp7w+NpwBdkJmpA= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= +github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/muesli/smartcrop v0.3.0 h1:JTlSkmxWg/oQ1TcLDoypuirdE8Y/jzNirQeLkxpA6Oc= +github.com/muesli/smartcrop v0.3.0/go.mod h1:i2fCI/UorTfgEpPPLWiFBv4pye+YAG78RwcQLUkocpI= +github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek= +github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o= +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/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +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/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk= github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +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/tdewolff/minify/v2 v2.23.5 h1:/P548KcpTkIOUvNg22zN83/GiaYSOIrbqtoue4I7kYM= +github.com/tdewolff/minify/v2 v2.23.5/go.mod h1:2RI9tiIrzJU1Z5EasXEPaI1MqobRyxKHOOgrRkq5oEw= github.com/tdewolff/parse/v2 v2.8.1 h1:J5GSHru6o3jF1uLlEKVXkDxxcVx6yzOlIVIotK4w2po= github.com/tdewolff/parse/v2 v2.8.1/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo= +github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE= github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= +github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I= +github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= +github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo= +github.com/yuin/goldmark v1.7.11/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs= +github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= +golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA= +golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= +golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w= +golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= +rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= diff --git a/.citools/src/bra/go.mod b/.citools/src/bra/go.mod index fec1ff55bbb..43a1908f9c4 100644 --- a/.citools/src/bra/go.mod +++ b/.citools/src/bra/go.mod @@ -1,6 +1,6 @@ module bra -go 1.24.5 +go 1.25.3 tool github.com/unknwon/bra @@ -17,6 +17,6 @@ require ( github.com/unknwon/com v1.0.1 // indirect github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a // indirect github.com/urfave/cli v1.22.16 // indirect - golang.org/x/sys v0.33.0 // indirect + golang.org/x/sys v0.36.0 // indirect gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect ) diff --git a/.citools/src/bra/go.sum b/.citools/src/bra/go.sum index 601cd7a8f3f..723e36ed099 100644 --- a/.citools/src/bra/go.sum +++ b/.citools/src/bra/go.sum @@ -56,8 +56,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/.citools/src/cog/go.mod b/.citools/src/cog/go.mod index 1b0f1246663..0ee1004f974 100644 --- a/.citools/src/cog/go.mod +++ b/.citools/src/cog/go.mod @@ -1,6 +1,6 @@ module cog -go 1.24.5 +go 1.25.3 tool github.com/grafana/cog/cmd/cli @@ -40,11 +40,11 @@ require ( github.com/spf13/pflag v1.0.6 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/yalue/merged_fs v1.3.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.36.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/src/cog/go.sum b/.citools/src/cog/go.sum index 513236e89f4..fc55dd4132c 100644 --- a/.citools/src/cog/go.sum +++ b/.citools/src/cog/go.sum @@ -85,20 +85,20 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 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.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= 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= diff --git a/.citools/src/cue/go.mod b/.citools/src/cue/go.mod index d2b53be05ce..d9a143c75c2 100644 --- a/.citools/src/cue/go.mod +++ b/.citools/src/cue/go.mod @@ -1,6 +1,6 @@ module cue -go 1.24.5 +go 1.25.3 tool cuelang.org/go/cmd/cue @@ -25,13 +25,13 @@ require ( github.com/spf13/pflag v1.0.6 // indirect github.com/stretchr/testify v1.10.0 // indirect github.com/tetratelabs/wazero v1.6.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.40.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.36.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/src/cue/go.sum b/.citools/src/cue/go.sum index ff7385db2e7..86c2675545b 100644 --- a/.citools/src/cue/go.sum +++ b/.citools/src/cue/go.sum @@ -53,20 +53,20 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g= github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= 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= diff --git a/.citools/src/golangci-lint/go.mod b/.citools/src/golangci-lint/go.mod index b5152308c30..d3ac9b697aa 100644 --- a/.citools/src/golangci-lint/go.mod +++ b/.citools/src/golangci-lint/go.mod @@ -1,41 +1,48 @@ module golangci-lint -go 1.24.5 +go 1.25.3 tool github.com/golangci/golangci-lint/v2/cmd/golangci-lint require ( 4d63.com/gocheckcompilerdirectives v1.3.0 // indirect 4d63.com/gochecknoglobals v0.2.2 // indirect - github.com/4meepo/tagalign v1.4.2 // indirect - github.com/Abirdcfly/dupword v0.1.3 // indirect - github.com/Antonboom/errname v1.1.0 // indirect - github.com/Antonboom/nilnil v1.1.0 // indirect - github.com/Antonboom/testifylint v1.6.0 // indirect + codeberg.org/chavacava/garif v0.2.0 // indirect + dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect + dev.gaijin.team/go/golib v0.6.0 // indirect + github.com/4meepo/tagalign v1.4.3 // indirect + github.com/Abirdcfly/dupword v0.1.6 // indirect + github.com/AdminBenni/iota-mixing v1.0.0 // indirect + github.com/AlwxSin/noinlineerr v1.0.5 // indirect + github.com/Antonboom/errname v1.1.1 // indirect + github.com/Antonboom/nilnil v1.1.1 // indirect + github.com/Antonboom/testifylint v1.6.4 // indirect github.com/BurntSushi/toml v1.5.0 // indirect - github.com/Crocmagnon/fatcontext v0.7.1 // indirect - github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect - github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 // indirect + github.com/Djarvur/go-err113 v0.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/MirrexOne/unqueryvet v1.2.1 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect + github.com/alecthomas/chroma/v2 v2.20.0 // indirect github.com/alecthomas/go-check-sumtype v0.3.1 // indirect - github.com/alexkohler/nakedret/v2 v2.0.5 // indirect + github.com/alexkohler/nakedret/v2 v2.0.6 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect + github.com/alfatraining/structtag v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect - github.com/alingse/nilnesserr v0.1.2 // indirect - github.com/ashanbrown/forbidigo v1.6.0 // indirect - github.com/ashanbrown/makezero v1.2.0 // indirect + github.com/alingse/nilnesserr v0.2.0 // indirect + github.com/ashanbrown/forbidigo/v2 v2.1.0 // indirect + github.com/ashanbrown/makezero/v2 v2.0.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bkielbasa/cyclop v1.2.3 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect - github.com/bombsimon/wsl/v4 v4.6.0 // indirect + github.com/bombsimon/wsl/v4 v4.7.0 // indirect + github.com/bombsimon/wsl/v5 v5.2.0 // indirect github.com/breml/bidichk v0.3.3 // indirect github.com/breml/errchkjson v0.4.1 // indirect - github.com/butuzov/ireturn v0.3.1 // indirect + github.com/butuzov/ireturn v0.4.0 // indirect github.com/butuzov/mirror v1.3.0 // indirect github.com/catenacyber/perfsprint v0.9.1 // indirect - github.com/ccojocar/zxcvbn-go v1.0.2 // indirect + github.com/ccojocar/zxcvbn-go v1.0.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.10 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect @@ -43,20 +50,20 @@ require ( github.com/charmbracelet/x/ansi v0.8.0 // indirect github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect - github.com/chavacava/garif v0.1.0 // indirect github.com/ckaznocha/intrange v0.3.1 // indirect github.com/curioswitch/go-reassign v0.3.0 // indirect - github.com/daixiang0/gci v0.13.6 // indirect + github.com/daixiang0/gci v0.13.7 // indirect github.com/dave/dst v0.27.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fatih/structtag v1.2.0 // indirect - github.com/firefart/nonamedreturns v1.0.5 // indirect + github.com/firefart/nonamedreturns v1.0.6 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/ghostiam/protogetter v0.3.12 // indirect + github.com/ghostiam/protogetter v0.3.16 // indirect github.com/go-critic/go-critic v0.13.0 // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect github.com/go-toolsmith/astcopy v1.1.0 // indirect @@ -68,46 +75,50 @@ require ( github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect github.com/gobwas/glob v0.2.3 // indirect + github.com/godoc-lint/godoc-lint v0.10.0 // indirect github.com/gofrs/flock v0.12.1 // indirect + github.com/golangci/asciicheck v0.5.0 // indirect github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect - github.com/golangci/go-printf-func-name v0.1.0 // indirect + github.com/golangci/go-printf-func-name v0.1.1 // indirect github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect - github.com/golangci/golangci-lint/v2 v2.0.2 // indirect + github.com/golangci/golangci-lint/v2 v2.5.0 // indirect github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 // indirect - github.com/golangci/misspell v0.6.0 // indirect - github.com/golangci/plugin-module-register v0.1.1 // indirect + github.com/golangci/misspell v0.7.0 // indirect + github.com/golangci/nilerr v0.0.0-20250918000102-015671e622fe // indirect + github.com/golangci/plugin-module-register v0.1.2 // indirect github.com/golangci/revgrep v0.8.0 // indirect - github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect + github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect + github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect - github.com/gordonklaus/ineffassign v0.1.0 // indirect + github.com/gordonklaus/ineffassign v0.2.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.5.0 // indirect github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect - github.com/gostaticanalysis/nilerr v0.1.1 // indirect github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jgautheron/goconst v1.7.1 // indirect + github.com/jgautheron/goconst v1.8.2 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect - github.com/jjti/go-spancheck v0.6.4 // indirect + github.com/jjti/go-spancheck v0.6.5 // indirect github.com/julz/importas v0.2.0 // indirect github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect github.com/kisielk/errcheck v1.9.0 // indirect github.com/kkHAIKE/contextcheck v1.1.6 // indirect - github.com/kulti/thelper v0.6.3 // indirect - github.com/kunwardeep/paralleltest v1.0.10 // indirect + github.com/kulti/thelper v0.7.1 // indirect + github.com/kunwardeep/paralleltest v1.0.14 // indirect github.com/lasiar/canonicalheader v1.1.2 // indirect - github.com/ldez/exptostd v0.4.2 // indirect - github.com/ldez/gomoddirectives v0.6.1 // indirect - github.com/ldez/grignotin v0.9.0 // indirect - github.com/ldez/tagliatelle v0.7.1 // indirect - github.com/ldez/usetesting v0.4.2 // indirect + github.com/ldez/exptostd v0.4.4 // indirect + github.com/ldez/gomoddirectives v0.7.0 // indirect + github.com/ldez/grignotin v0.10.1 // indirect + github.com/ldez/tagliatelle v0.7.2 // indirect + github.com/ldez/usetesting v0.5.0 // indirect github.com/leonklingele/grouper v1.1.2 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/macabu/inamedparam v0.2.0 // indirect + github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect + github.com/manuelarte/funcorder v0.5.0 // indirect github.com/maratori/testableexamples v1.0.0 // indirect github.com/maratori/testpackage v1.1.1 // indirect github.com/matoous/godox v1.1.0 // indirect @@ -115,7 +126,7 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/mgechev/revive v1.7.0 // indirect + github.com/mgechev/revive v1.12.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/moricho/tparallel v0.3.2 // indirect github.com/muesli/termenv v0.16.0 // indirect @@ -123,11 +134,10 @@ require ( github.com/nakabonne/nestif v0.3.1 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.19.1 // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/nunnatsa/ginkgolinter v0.21.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/polyfloyd/go-errorlint v1.7.1 // indirect + github.com/polyfloyd/go-errorlint v1.8.0 // indirect github.com/prometheus/client_golang v1.22.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.63.0 // indirect @@ -144,58 +154,59 @@ require ( github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect - github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect - github.com/sashamelentyev/usestdlibvars v1.28.0 // indirect - github.com/securego/gosec/v2 v2.22.2 // indirect + github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect + github.com/securego/gosec/v2 v2.22.8 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sivchari/containedctx v1.0.3 // indirect - github.com/sonatard/noctx v0.1.0 // indirect + github.com/sonatard/noctx v0.4.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect - github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/afero v1.14.0 // indirect github.com/spf13/cast v1.7.1 // indirect - github.com/spf13/cobra v1.9.1 // indirect - github.com/spf13/pflag v1.0.6 // indirect + github.com/spf13/cobra v1.10.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper v1.20.1 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/stretchr/testify v1.10.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tdakkota/asciicheck v0.4.1 // indirect - github.com/tetafro/godot v1.5.0 // indirect + github.com/tetafro/godot v1.5.4 // indirect github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect - github.com/timonwong/loggercheck v0.10.1 // indirect - github.com/tomarrell/wrapcheck/v2 v2.10.0 // indirect + github.com/timonwong/loggercheck v0.11.0 // indirect + github.com/tomarrell/wrapcheck/v2 v2.11.0 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect github.com/ultraware/funlen v0.2.0 // indirect github.com/ultraware/whitespace v0.2.0 // indirect github.com/uudashr/gocognit v1.2.0 // indirect - github.com/uudashr/iface v1.3.1 // indirect + github.com/uudashr/iface v1.4.1 // indirect github.com/xen0n/gosmopolitan v1.3.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect github.com/ykadowak/zerologlint v0.1.5 // indirect gitlab.com/bosi/decorder v0.4.2 // indirect - go-simpler.org/musttag v0.13.0 // indirect - go-simpler.org/sloglint v0.9.0 // indirect + go-simpler.org/musttag v0.14.0 // indirect + go-simpler.org/sloglint v0.11.1 // indirect + go.augendre.info/arangolint v0.2.0 // indirect + go.augendre.info/fatcontext v0.8.1 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect - golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/net v0.45.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.37.0 // indirect google.golang.org/protobuf v1.36.6 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.6.1 // indirect - mvdan.cc/gofumpt v0.7.0 // indirect + mvdan.cc/gofumpt v0.9.1 // indirect mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect ) diff --git a/.citools/src/golangci-lint/go.sum b/.citools/src/golangci-lint/go.sum index 37d9819bfeb..4748f406fb8 100644 --- a/.citools/src/golangci-lint/go.sum +++ b/.citools/src/golangci-lint/go.sum @@ -2,46 +2,58 @@ 4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY= 4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU= 4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0= -github.com/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E= -github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI= -github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE= -github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw= -github.com/Antonboom/errname v1.1.0 h1:A+ucvdpMwlo/myWrkHEUEBWc/xuXdud23S8tmTb/oAE= -github.com/Antonboom/errname v1.1.0/go.mod h1:O1NMrzgUcVBGIfi3xlVuvX8Q/VP/73sseCaAppfjqZw= -github.com/Antonboom/nilnil v1.1.0 h1:jGxJxjgYS3VUUtOTNk8Z1icwT5ESpLH/426fjmQG+ng= -github.com/Antonboom/nilnil v1.1.0/go.mod h1:b7sAlogQjFa1wV8jUW3o4PMzDVFLbTux+xnQdvzdcIE= -github.com/Antonboom/testifylint v1.6.0 h1:6rdILVPt4+rqcvhid8w9wJNynKLUgqHNpFyM67UeXyc= -github.com/Antonboom/testifylint v1.6.0/go.mod h1:k+nEkathI2NFjKO6HvwmSrbzUcQ6FAnbZV+ZRrnXPLI= +codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6MfY= +codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ= +dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKfwODoI1Y= +dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI= +dev.gaijin.team/go/golib v0.6.0 h1:v6nnznFTs4bppib/NyU1PQxobwDHwCXXl15P7DV5Zgo= +dev.gaijin.team/go/golib v0.6.0/go.mod h1:uY1mShx8Z/aNHWDyAkZTkX+uCi5PdX7KsG1eDQa2AVE= +github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8= +github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c= +github.com/Abirdcfly/dupword v0.1.6 h1:qeL6u0442RPRe3mcaLcbaCi2/Y/hOcdtw6DE9odjz9c= +github.com/Abirdcfly/dupword v0.1.6/go.mod h1:s+BFMuL/I4YSiFv29snqyjwzDp4b65W2Kvy+PKzZ6cw= +github.com/AdminBenni/iota-mixing v1.0.0 h1:Os6lpjG2dp/AE5fYBPAA1zfa2qMdCAWwPMCgpwKq7wo= +github.com/AdminBenni/iota-mixing v1.0.0/go.mod h1:i4+tpAaB+qMVIV9OK3m4/DAynOd5bQFaOu+2AhtBCNY= +github.com/AlwxSin/noinlineerr v1.0.5 h1:RUjt63wk1AYWTXtVXbSqemlbVTb23JOSRiNsshj7TbY= +github.com/AlwxSin/noinlineerr v1.0.5/go.mod h1:+QgkkoYrMH7RHvcdxdlI7vYYEdgeoFOVjU9sUhw/rQc= +github.com/Antonboom/errname v1.1.1 h1:bllB7mlIbTVzO9jmSWVWLjxTEbGBVQ1Ff/ClQgtPw9Q= +github.com/Antonboom/errname v1.1.1/go.mod h1:gjhe24xoxXp0ScLtHzjiXp0Exi1RFLKJb0bVBtWKCWQ= +github.com/Antonboom/nilnil v1.1.1 h1:9Mdr6BYd8WHCDngQnNVV0b554xyisFioEKi30sksufQ= +github.com/Antonboom/nilnil v1.1.1/go.mod h1:yCyAmSw3doopbOWhJlVci+HuyNRuHJKIv6V2oYQa8II= +github.com/Antonboom/testifylint v1.6.4 h1:gs9fUEy+egzxkEbq9P4cpcMB6/G0DYdMeiFS87UiqmQ= +github.com/Antonboom/testifylint v1.6.4/go.mod h1:YO33FROXX2OoUfwjz8g+gUxQXio5i9qpVy7nXGbxDD4= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/Crocmagnon/fatcontext v0.7.1 h1:SC/VIbRRZQeQWj/TcQBS6JmrXcfA+BU4OGSVUt54PjM= -github.com/Crocmagnon/fatcontext v0.7.1/go.mod h1:1wMvv3NXEBJucFGfwOJBxSVWcoIO6emV215SMkW9MFU= -github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= -github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 h1:Sz1JIXEcSfhz7fUi7xHnhpIE0thVASYjvosApmHuD2k= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1/go.mod h1:n/LSCXNuIYqVfBlVXyHfMQkZDdp1/mmxfSjADd3z1Zg= +github.com/Djarvur/go-err113 v0.1.1 h1:eHfopDqXRwAi+YmCUas75ZE0+hoBHJ2GQNLYRSxao4g= +github.com/Djarvur/go-err113 v0.1.1/go.mod h1:IaWJdYFLg76t2ihfflPZnM1LIQszWOsFDh2hhhAVF6k= github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/MirrexOne/unqueryvet v1.2.1 h1:M+zdXMq84g+E1YOLa7g7ExN3dWfZQrdDSTCM7gC+m/A= +github.com/MirrexOne/unqueryvet v1.2.1/go.mod h1:IWwCwMQlSWjAIteW0t+28Q5vouyktfujzYznSIWiuOg= github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4= github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw= +github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA= github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU= github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E= -github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= -github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU= -github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= +github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg= +github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alexkohler/nakedret/v2 v2.0.6 h1:ME3Qef1/KIKr3kWX3nti3hhgNxw6aqN5pZmQiFSsuzQ= +github.com/alexkohler/nakedret/v2 v2.0.6/go.mod h1:l3RKju/IzOMQHmsEvXwkqMDzHHvurNQfAgE1eVmT40Q= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= +github.com/alfatraining/structtag v1.0.0 h1:2qmcUqNcCoyVJ0up879K614L9PazjBSFruTB0GOFjCc= +github.com/alfatraining/structtag v1.0.0/go.mod h1:p3Xi5SwzTi+Ryj64DqjLWz7XurHxbGsq6y3ubePJPus= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= -github.com/alingse/nilnesserr v0.1.2 h1:Yf8Iwm3z2hUUrP4muWfW83DF4nE3r1xZ26fGWUKCZlo= -github.com/alingse/nilnesserr v0.1.2/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= -github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= -github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= -github.com/ashanbrown/makezero v1.2.0 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU= -github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4= +github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w= +github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= +github.com/ashanbrown/forbidigo/v2 v2.1.0 h1:NAxZrWqNUQiDz19FKScQ/xvwzmij6BiOw3S0+QUQ+Hs= +github.com/ashanbrown/forbidigo/v2 v2.1.0/go.mod h1:0zZfdNAuZIL7rSComLGthgc/9/n2FqspBOH90xlCHdA= +github.com/ashanbrown/makezero/v2 v2.0.1 h1:r8GtKetWOgoJ4sLyUx97UTwyt2dO7WkGFHizn/Lo8TY= +github.com/ashanbrown/makezero/v2 v2.0.1/go.mod h1:kKU4IMxmYW1M4fiEHMb2vc5SFoPzXvgbMR9gIp5pjSw= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -50,20 +62,22 @@ github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5 github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= -github.com/bombsimon/wsl/v4 v4.6.0 h1:ew2R/N42su553DKTYqt3HSxaQN+uHQPv4xZ2MBmwaW4= -github.com/bombsimon/wsl/v4 v4.6.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg= +github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ= +github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg= +github.com/bombsimon/wsl/v5 v5.2.0 h1:PyCCwd3Q7abGs3e34IW4jLYlBS+FbsU6iK+Tb3NnDp4= +github.com/bombsimon/wsl/v5 v5.2.0/go.mod h1:Gp8lD04z27wm3FANIUPZycXp+8huVsn0oxc+n4qfV9I= github.com/breml/bidichk v0.3.3 h1:WSM67ztRusf1sMoqH6/c4OBCUlRVTKq+CbSeo0R17sE= github.com/breml/bidichk v0.3.3/go.mod h1:ISbsut8OnjB367j5NseXEGGgO/th206dVa427kR8YTE= github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDwg= github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s= -github.com/butuzov/ireturn v0.3.1 h1:mFgbEI6m+9W8oP/oDdfA34dLisRFCj2G6o/yiI1yZrY= -github.com/butuzov/ireturn v0.3.1/go.mod h1:ZfRp+E7eJLC0NQmk1Nrm1LOrn/gQlOykv+cVPdiXH5M= +github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E= +github.com/butuzov/ireturn v0.4.0/go.mod h1:ghI0FrCmap8pDWZwfPisFD1vEc56VKH4NpQUxDHta70= github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc= github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI= github.com/catenacyber/perfsprint v0.9.1 h1:5LlTp4RwTooQjJCvGEFV6XksZvWE7wCOUvjD2z0vls0= github.com/catenacyber/perfsprint v0.9.1/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM= -github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg= -github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= +github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc= +github.com/ccojocar/zxcvbn-go v1.0.4/go.mod h1:3GxGX+rHmueTUMvm5ium7irpyjmm7ikxYFOSJB21Das= 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/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= @@ -78,15 +92,13 @@ github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0G github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= -github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= -github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs= github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs= github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= -github.com/daixiang0/gci v0.13.6 h1:RKuEOSkGpSadkGbvZ6hJ4ddItT3cVZ9Vn9Rybk6xjl8= -github.com/daixiang0/gci v0.13.6/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= +github.com/daixiang0/gci v0.13.7 h1:+0bG5eK9vlI08J+J/NWGbWPTNiXPG4WhNLJOkSxWITQ= +github.com/daixiang0/gci v0.13.7/go.mod h1:812WVN6JLFY9S6Tv76twqmNqevN0pa3SX3nih0brVzQ= github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY= github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= @@ -97,28 +109,28 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= -github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= -github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= -github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw= +github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E= +github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/ghostiam/protogetter v0.3.12 h1:xTPjH97iKph27vXRRKV0OCke5sAMoHPbVeVstdzmCLE= -github.com/ghostiam/protogetter v0.3.12/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= +github.com/ghostiam/protogetter v0.3.16 h1:UkrisuJBYLnZW6FcYUNBDJOqY3X22RtoYMlCsiNlFFA= +github.com/ghostiam/protogetter v0.3.16/go.mod h1:4SRRIv6PcjkIMpUkRUsP4TsUTqO/N3Fmvwivuc/sCHA= github.com/go-critic/go-critic v0.13.0 h1:kJzM7wzltQasSUXtYyTl6UaPVySO6GkaR1thFnJ6afY= github.com/go-critic/go-critic v0.13.0/go.mod h1:M/YeuJ3vOCQDnP2SU+ZhjgRzwzcBW87JqLpMJLrZDLI= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +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-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -148,47 +160,50 @@ github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUW github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/godoc-lint/godoc-lint v0.10.0 h1:OcyrziBi18sQSEpib6NesVHEJ/Xcng97NunePBA48g4= +github.com/godoc-lint/godoc-lint v0.10.0/go.mod h1:KleLcHu/CGSvkjUH2RvZyoK1MBC7pDQg4NxMYLcBBsw= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/golangci/asciicheck v0.5.0 h1:jczN/BorERZwK8oiFBOGvlGPknhvq0bjnysTj4nUfo0= +github.com/golangci/asciicheck v0.5.0/go.mod h1:5RMNAInbNFw2krqN6ibBxN/zfRFa9S6tA1nPdM0l8qQ= github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 h1:WUvBfQL6EW/40l6OmeSBYQJNSif4O11+bmWEz+C7FYw= github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E= -github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUPPyAKJuzv8pEJU= -github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s= +github.com/golangci/go-printf-func-name v0.1.1 h1:hIYTFJqAGp1iwoIfsNTpoq1xZAarogrvjO9AfiW3B4U= +github.com/golangci/go-printf-func-name v0.1.1/go.mod h1:Es64MpWEZbh0UBtTAICOZiB+miW53w/K9Or/4QogJss= github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE= github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY= -github.com/golangci/golangci-lint/v2 v2.0.2 h1:dMCC8ikPiLDvHMFy3+XypSAuGDBOLzwWqqamer+bWsY= -github.com/golangci/golangci-lint/v2 v2.0.2/go.mod h1:ptNNMeGBQrbves0Qq38xvfdJg18PzxmT+7KRCOpm6i8= +github.com/golangci/golangci-lint/v2 v2.5.0 h1:BDRg4ASm4J1y/DSRY6zwJ5tr5Yy8ZqbZ79XrCeFxaQo= +github.com/golangci/golangci-lint/v2 v2.5.0/go.mod h1:IJtWJBZkLbx7AVrIUzLd8Oi3ADtwaNpWbR3wthVWHcc= github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 h1:AkK+w9FZBXlU/xUmBtSJN1+tAI4FIvy5WtnUnY8e4p8= github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95/go.mod h1:k9mmcyWKSTMcPPvQUCfRWWQ9VHJ1U9Dc0R7kaXAgtnQ= -github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= -github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= -github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c= -github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc= +github.com/golangci/misspell v0.7.0 h1:4GOHr/T1lTW0hhR4tgaaV1WS/lJ+ncvYCoFKmqJsj0c= +github.com/golangci/misspell v0.7.0/go.mod h1:WZyyI2P3hxPY2UVHs3cS8YcllAeyfquQcKfdeE9AFVg= +github.com/golangci/nilerr v0.0.0-20250918000102-015671e622fe h1:F1pK9tBy41i7eesBFkSNMldwtiAaWiU+3fT/24sTnNI= +github.com/golangci/nilerr v0.0.0-20250918000102-015671e622fe/go.mod h1:CtTxAluxD2ng9aIT9bPrVoMuISFWCD+SaxtvYtdWA2k= +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s= github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= -github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs= -github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2bRA5htgAG9r7s3tHsfjIhN98WshBTJ9jM= +github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s= +github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM= +github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= -github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= -github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= -github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= +github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18= +github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= +github.com/gordonklaus/ineffassign v0.2.0 h1:Uths4KnmwxNJNzq87fwQQDDnbNb7De00VOk9Nu0TySs= +github.com/gordonklaus/ineffassign v0.2.0/go.mod h1:TIpymnagPSexySzs7F9FnO1XFTy8IT3a59vmZp5Y9Lw= github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= -github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= github.com/gostaticanalysis/comment v1.5.0 h1:X82FLl+TswsUMpMh17srGRuKaaXprTaytmEpgnKIDu8= github.com/gostaticanalysis/comment v1.5.0/go.mod h1:V6eb3gpCv9GNVqb6amXzEUX3jXLVK/AdA+IrAMSqvEc= github.com/gostaticanalysis/forcetypeassert v0.2.0 h1:uSnWrrUEYDr86OCxWa4/Tp2jeYDlogZiZHzGkWFefTk= github.com/gostaticanalysis/forcetypeassert v0.2.0/go.mod h1:M5iPavzE9pPqWyeiVXSFghQjljW1+l/Uke3PXHS6ILY= -github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk= -github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8= github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs= @@ -205,12 +220,12 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= -github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jgautheron/goconst v1.8.2 h1:y0XF7X8CikZ93fSNT6WBTb/NElBu9IjaY7CCYQrCMX4= +github.com/jgautheron/goconst v1.8.2/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= -github.com/jjti/go-spancheck v0.6.4 h1:Tl7gQpYf4/TMU7AT84MN83/6PutY21Nb9fuQjFTpRRc= -github.com/jjti/go-spancheck v0.6.4/go.mod h1:yAEYdKJ2lRkDA8g7X+oKUHXOWVAXSBJRv04OhF+QUjk= +github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgYB8= +github.com/jjti/go-spancheck v0.6.5/go.mod h1:aEogkeatBrbYsyW6y5TgDfihCulDYciL1B7rG2vSsrU= github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY= github.com/karamaru-alpha/copyloopvar v1.2.1 h1:wmZaZYIjnJ0b5UoKDjUHrikcV0zuPyyxI4SVplLd2CI= @@ -226,28 +241,32 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= -github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= -github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs= -github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= +github.com/kulti/thelper v0.7.1 h1:fI8QITAoFVLx+y+vSyuLBP+rcVIB8jKooNSCT2EiI98= +github.com/kulti/thelper v0.7.1/go.mod h1:NsMjfQEy6sd+9Kfw8kCP61W1I0nerGSYSFnGaxQkcbs= +github.com/kunwardeep/paralleltest v1.0.14 h1:wAkMoMeGX/kGfhQBPODT/BL8XhK23ol/nuQ3SwFaUw8= +github.com/kunwardeep/paralleltest v1.0.14/go.mod h1:di4moFqtfz3ToSKxhNjhOZL+696QtJGCFe132CbBLGk= github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4= github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= -github.com/ldez/exptostd v0.4.2 h1:l5pOzHBz8mFOlbcifTxzfyYbgEmoUqjxLFHZkjlbHXs= -github.com/ldez/exptostd v0.4.2/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ= -github.com/ldez/gomoddirectives v0.6.1 h1:Z+PxGAY+217f/bSGjNZr/b2KTXcyYLgiWI6geMBN2Qc= -github.com/ldez/gomoddirectives v0.6.1/go.mod h1:cVBiu3AHR9V31em9u2kwfMKD43ayN5/XDgr+cdaFaKs= -github.com/ldez/grignotin v0.9.0 h1:MgOEmjZIVNn6p5wPaGp/0OKWyvq42KnzAt/DAb8O4Ow= -github.com/ldez/grignotin v0.9.0/go.mod h1:uaVTr0SoZ1KBii33c47O1M8Jp3OP3YDwhZCmzT9GHEk= -github.com/ldez/tagliatelle v0.7.1 h1:bTgKjjc2sQcsgPiT902+aadvMjCeMHrY7ly2XKFORIk= -github.com/ldez/tagliatelle v0.7.1/go.mod h1:3zjxUpsNB2aEZScWiZTHrAXOl1x25t3cRmzfK1mlo2I= -github.com/ldez/usetesting v0.4.2 h1:J2WwbrFGk3wx4cZwSMiCQQ00kjGR0+tuuyW0Lqm4lwA= -github.com/ldez/usetesting v0.4.2/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ= +github.com/ldez/exptostd v0.4.4 h1:58AtQjnLcT/tI5W/1KU7xE/O7zW9RAWB6c/ScQAnfus= +github.com/ldez/exptostd v0.4.4/go.mod h1:QfdzPw6oHjFVdNV7ILoPu5sw3OZ3OG1JS0I5JN3J4Js= +github.com/ldez/gomoddirectives v0.7.0 h1:EOx8Dd56BZYSez11LVgdj025lKwlP0/E5OLSl9HDwsY= +github.com/ldez/gomoddirectives v0.7.0/go.mod h1:wR4v8MN9J8kcwvrkzrx6sC9xe9Cp68gWYCsda5xvyGc= +github.com/ldez/grignotin v0.10.1 h1:keYi9rYsgbvqAZGI1liek5c+jv9UUjbvdj3Tbn5fn4o= +github.com/ldez/grignotin v0.10.1/go.mod h1:UlDbXFCARrXbWGNGP3S5vsysNXAPhnSuBufpTEbwOas= +github.com/ldez/tagliatelle v0.7.2 h1:KuOlL70/fu9paxuxbeqlicJnCspCRjH0x8FW+NfgYUk= +github.com/ldez/tagliatelle v0.7.2/go.mod h1:PtGgm163ZplJfZMZ2sf5nhUT170rSuPgBimoyYtdaSI= +github.com/ldez/usetesting v0.5.0 h1:3/QtzZObBKLy1F4F8jLuKJiKBjjVFi1IavpoWbmqLwc= +github.com/ldez/usetesting v0.5.0/go.mod h1:Spnb4Qppf8JTuRgblLrEWb7IE6rDmUpGvxY3iRrzvDQ= github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE= github.com/macabu/inamedparam v0.2.0/go.mod h1:+Pee9/YfGe5LJ62pYXqB89lJ+0k5bsR8Wgz/C0Zlq3U= +github.com/manuelarte/embeddedstructfieldcheck v0.4.0 h1:3mAIyaGRtjK6EO9E73JlXLtiy7ha80b2ZVGyacxgfww= +github.com/manuelarte/embeddedstructfieldcheck v0.4.0/go.mod h1:z8dFSyXqp+fC6NLDSljRJeNQJJDWnY7RoWFzV3PC6UM= +github.com/manuelarte/funcorder v0.5.0 h1:llMuHXXbg7tD0i/LNw8vGnkDTHFpTnWqKPI85Rknc+8= +github.com/manuelarte/funcorder v0.5.0/go.mod h1:Yt3CiUQthSBMBxjShjdXMexmzpP8YGvGLjrxJNkO2hA= github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= @@ -261,11 +280,10 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY= -github.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4= +github.com/mgechev/revive v1.12.0 h1:Q+/kkbbwerrVYPv9d9efaPGmAO/NsxwW/nE6ahpQaCU= +github.com/mgechev/revive v1.12.0/go.mod h1:VXsY2LsTigk8XU9BpZauVLjVrhICMOV3k1lpB3CXrp8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= @@ -280,14 +298,12 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.19.1 h1:mjwbOlDQxZi9Cal+KfbEJTCz327OLNfwNvoZ70NJ+c4= -github.com/nunnatsa/ginkgolinter v0.19.1/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= -github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= -github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= -github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/nunnatsa/ginkgolinter v0.21.0 h1:IYwuX+ajy3G1MezlMLB1BENRtFj16+Evyi4uki1NOOQ= +github.com/nunnatsa/ginkgolinter v0.21.0/go.mod h1:QlzY9UP9zaqu58FjYxhp9bnjuwXwG1bfW5rid9ChNMw= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= +github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY= +github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= @@ -295,13 +311,13 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= -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/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= 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/polyfloyd/go-errorlint v1.7.1 h1:RyLVXIbosq1gBdk/pChWA8zWYLsq9UEw7a1L5TVMCnA= -github.com/polyfloyd/go-errorlint v1.7.1/go.mod h1:aXjNb1x2TNhoLsk26iv1yl7a+zTnXPhwEMtEXukiLR8= +github.com/polyfloyd/go-errorlint v1.8.0 h1:DL4RestQqRLr8U4LygLw8g2DX6RN1eBJOpa2mzsrl1Q= +github.com/polyfloyd/go-errorlint v1.8.0/go.mod h1:G2W0Q5roxbLCt0ZQbdoxQxXktTjwNyDbEaj3n7jvl4s= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= @@ -338,14 +354,14 @@ github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsF github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0= github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= -github.com/sashamelentyev/usestdlibvars v1.28.0 h1:jZnudE2zKCtYlGzLVreNp5pmCdOxXUzwsMDBkR21cyQ= -github.com/sashamelentyev/usestdlibvars v1.28.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= -github.com/securego/gosec/v2 v2.22.2 h1:IXbuI7cJninj0nRpZSLCUlotsj8jGusohfONMrHoF6g= -github.com/securego/gosec/v2 v2.22.2/go.mod h1:UEBGA+dSKb+VqM6TdehR7lnQtIIMorYJ4/9CW1KVQBE= +github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ= +github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8= +github.com/securego/gosec/v2 v2.22.8 h1:3NMpmfXO8wAVFZPNsd3EscOTa32Jyo6FLLlW53bexMI= +github.com/securego/gosec/v2 v2.22.8/go.mod h1:ZAw8K2ikuH9qDlfdV87JmNghnVfKB1XC7+TVzk6Utto= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= @@ -354,21 +370,22 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= -github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM= -github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c= +github.com/sonatard/noctx v0.4.0 h1:7MC/5Gg4SQ4lhLYR6mvOP6mQVSxCrdyiExo7atBs27o= +github.com/sonatard/noctx v0.4.0/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas= 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/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= -github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= -github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= @@ -376,35 +393,27 @@ github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRk github.com/stbenjam/no-sprintf-host-port v0.2.0 h1:i8pxvGrt1+4G0czLr/WnmyH7zbZ8Bg8etvARQ1rpyl4= github.com/stbenjam/no-sprintf-host-port v0.2.0/go.mod h1:eL0bQ9PasS0hsyTyfTjjG+E80QIyPnBVQbYZyv20Jfk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8= -github.com/tdakkota/asciicheck v0.4.1/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.5.0 h1:aNwfVI4I3+gdxjMgYPus9eHmoBeJIbnajOyqZYStzuw= -github.com/tetafro/godot v1.5.0/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +github.com/tetafro/godot v1.5.4 h1:u1ww+gqpRLiIA16yF2PV1CV1n/X3zhyezbNXC3E14Sg= +github.com/tetafro/godot v1.5.4/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU= github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 h1:9LPGD+jzxMlnk5r6+hJnar67cgpDIz/iyD+rfl5r2Vk= github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460= -github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg= -github.com/timonwong/loggercheck v0.10.1/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= -github.com/tomarrell/wrapcheck/v2 v2.10.0 h1:SzRCryzy4IrAH7bVGG4cK40tNUhmVmMDuJujy4XwYDg= -github.com/tomarrell/wrapcheck/v2 v2.10.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= +github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M= +github.com/timonwong/loggercheck v0.11.0/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= +github.com/tomarrell/wrapcheck/v2 v2.11.0 h1:BJSt36snX9+4WTIXeJ7nvHBQBcm1h2SjQMSlmQ6aFSU= +github.com/tomarrell/wrapcheck/v2 v2.11.0/go.mod h1:wFL9pDWDAbXhhPZZt+nG8Fu+h29TtnZ2MW6Lx4BRXIU= github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/ultraware/funlen v0.2.0 h1:gCHmCn+d2/1SemTdYMiKLAHFYxTYz7z9VIDRaTGyLkI= @@ -413,8 +422,8 @@ github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSW github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYRA= github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU= -github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U= -github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg= +github.com/uudashr/iface v1.4.1 h1:J16Xl1wyNX9ofhpHmQ9h9gk5rnv2A6lX/2+APLTo0zU= +github.com/uudashr/iface v1.4.1/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg= github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM= github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= @@ -435,10 +444,14 @@ gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8= go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= -go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE= -go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM= -go-simpler.org/sloglint v0.9.0 h1:/40NQtjRx9txvsB/RN022KsUJU+zaaSb/9q9BSefSrE= -go-simpler.org/sloglint v0.9.0/go.mod h1:G/OrAF6uxj48sHahCzrbarVMptL2kjWTaUeC8+fOGww= +go-simpler.org/musttag v0.14.0 h1:XGySZATqQYSEV3/YTy+iX+aofbZZllJaqwFWs+RTtSo= +go-simpler.org/musttag v0.14.0/go.mod h1:uP8EymctQjJ4Z1kUnjX0u2l60WfUdQxCwSNKzE1JEOE= +go-simpler.org/sloglint v0.11.1 h1:xRbPepLT/MHPTCA6TS/wNfZrDzkGvCCqUv4Bdwc3H7s= +go-simpler.org/sloglint v0.11.1/go.mod h1:2PowwiCOK8mjiF+0KGifVOT8ZsCNiFzvfyJeJOIt8MQ= +go.augendre.info/arangolint v0.2.0 h1:2NP/XudpPmfBhQKX4rMk+zDYIj//qbt4hfZmSSTcpj8= +go.augendre.info/arangolint v0.2.0/go.mod h1:Vx4KSJwu48tkE+8uxuf0cbBnAPgnt8O1KWiT7bljq7w= +go.augendre.info/fatcontext v0.8.1 h1:/T4+cCjpL9g71gJpcFAgVo/K5VFpqlN+NPU7QXxD5+A= +go.augendre.info/fatcontext v0.8.1/go.mod h1:r3Qz4ZOzex66wfyyj5VZ1xUcl81vzvHQ6/GWzzlMEwA= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -457,21 +470,19 @@ golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4= -golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621 h1:Yl4H5w2RV7L/dvSHp2GerziT5K2CORgFINPaMFxWGWw= +golang.org/x/exp/typeparams v0.0.0-20250911091902-df9299821621/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -481,14 +492,12 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= 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-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -498,8 +507,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -515,19 +524,16 @@ golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= @@ -536,33 +542,29 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= 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-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= +golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -574,14 +576,13 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 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.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI= honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= -mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= -mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= +mvdan.cc/gofumpt v0.9.1 h1:p5YT2NfFWsYyTieYgwcQ8aKV3xRvFH4uuN/zB2gBbMQ= +mvdan.cc/gofumpt v0.9.1/go.mod h1:3xYtNemnKiXaTh6R4VtlqDATFwBbdXI8lJvH/4qk7mw= mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8= mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5ginj6dA2oLE7YNlta9qhBNNdCaLE= diff --git a/.citools/src/jb/go.mod b/.citools/src/jb/go.mod index 05208bc103e..96fe587d14f 100644 --- a/.citools/src/jb/go.mod +++ b/.citools/src/jb/go.mod @@ -1,6 +1,6 @@ module jb -go 1.24.5 +go 1.25.3 tool github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb @@ -15,6 +15,6 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/stretchr/testify v1.10.0 // indirect - golang.org/x/sys v0.33.0 // indirect + golang.org/x/sys v0.36.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect ) diff --git a/.citools/src/jb/go.sum b/.citools/src/jb/go.sum index 90354357864..82c114a77ad 100644 --- a/.citools/src/jb/go.sum +++ b/.citools/src/jb/go.sum @@ -54,8 +54,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/.citools/src/lefthook/go.mod b/.citools/src/lefthook/go.mod index e45942a7dc1..e37e46a5ea6 100644 --- a/.citools/src/lefthook/go.mod +++ b/.citools/src/lefthook/go.mod @@ -1,6 +1,6 @@ module lefthook -go 1.24.5 +go 1.25.3 tool github.com/evilmartians/lefthook @@ -43,10 +43,9 @@ require ( github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/term v0.32.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/term v0.35.0 // indirect + golang.org/x/text v0.29.0 // indirect gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/.citools/src/lefthook/go.sum b/.citools/src/lefthook/go.sum index b6b9533af75..c0e796f4ae9 100644 --- a/.citools/src/lefthook/go.sum +++ b/.citools/src/lefthook/go.sum @@ -91,14 +91,14 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61 h1:8ajkpB4hXVftY5ko905id+dOnmorcS2CHNxxHLLDcFM= gopkg.in/alessio/shellescape.v1 v1.0.0-20170105083845-52074bc9df61/go.mod h1:IfMagxm39Ys4ybJrDb7W3Ob8RwxftP0Yy+or/NVz1O8= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/.citools/src/swagger/go.mod b/.citools/src/swagger/go.mod index 4d169e867f2..d0ed2ce2bcf 100644 --- a/.citools/src/swagger/go.mod +++ b/.citools/src/swagger/go.mod @@ -1,6 +1,6 @@ module swagger -go 1.24.5 +go 1.25.3 tool github.com/go-swagger/go-swagger/cmd/swagger @@ -51,12 +51,12 @@ require ( github.com/toqueteos/webbrowser v1.2.0 // indirect go.mongodb.org/mongo-driver v1.16.1 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.36.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/.citools/src/swagger/go.sum b/.citools/src/swagger/go.sum index f5342e8f329..dff45a5ee81 100644 --- a/.citools/src/swagger/go.sum +++ b/.citools/src/swagger/go.sum @@ -101,19 +101,19 @@ go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4 go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= 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= diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dd7c1d87290..b9daf7a2487 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -302,7 +302,7 @@ /devenv/docker/blocks/prometheus_random_data/ @grafana/oss-big-tent /devenv/docker/blocks/prometheus_high_card/ @grafana/oss-big-tent /devenv/docker/blocks/prometheus_utf8/ @grafana/oss-big-tent -/devenv/docker/blocks/pyroscope/ @grafana/observability-traces-and-profiling +/devenv/docker/blocks/pyroscope/ @grafana/oss-big-tent /devenv/docker/blocks/redis/ @bergquist /devenv/docker/blocks/sensugo/ @grafana/grafana-backend-group /devenv/docker/blocks/slow_proxy/ @bergquist @@ -352,8 +352,8 @@ /pkg/tsdb/prometheus/ @grafana/oss-big-tent /pkg/tsdb/elasticsearch/ @grafana/partner-datasources /pkg/tsdb/loki/ @grafana/oss-big-tent -/pkg/tsdb/tempo/ @grafana/oss-big-tent @grafana/observability-traces-and-profiling -/pkg/tsdb/grafana-pyroscope-datasource/ @grafana/observability-traces-and-profiling +/pkg/tsdb/tempo/ @grafana/oss-big-tent +/pkg/tsdb/grafana-pyroscope-datasource/ @grafana/oss-big-tent /pkg/tsdb/parca/ @grafana/oss-big-tent # OSS Big Tent backend code @@ -429,6 +429,7 @@ /e2e-playwright/dashboards/TestDashboard.json @grafana/dashboards-squad @grafana/grafana-search-navigate-organise /e2e-playwright/dashboards/TestV2Dashboard.json @grafana/dashboards-squad /e2e-playwright/dashboards/V2DashWithRepeats.json @grafana/dashboards-squad +/e2e-playwright/dashboards/V2DashWithTabRepeats.json @grafana/dashboards-squad /e2e-playwright/dashboards-suite/adhoc-filter-from-panel.spec.ts @grafana/datapro /e2e-playwright/dashboards-suite/dashboard-browse-nested.spec.ts @grafana/grafana-search-navigate-organise /e2e-playwright/dashboards-suite/dashboard-browse.spec.ts @grafana/grafana-search-navigate-organise @@ -609,7 +610,7 @@ /packages/grafana-o11y-ds-frontend/ @grafana/observability-logs /packages/grafana-o11y-ds-frontend/src/IntervalInput/ @grafana/observability-traces-and-profiling /packages/grafana-o11y-ds-frontend/src/NodeGraph/ @grafana/observability-traces-and-profiling -/packages/grafana-o11y-ds-frontend/src/pyroscope/ @grafana/observability-traces-and-profiling +/packages/grafana-o11y-ds-frontend/src/pyroscope/ @grafana/oss-big-tent @grafana/observability-traces-and-profiling /packages/grafana-o11y-ds-frontend/src/SpanBar/ @grafana/oss-big-tent @grafana/observability-traces-and-profiling /packages/grafana-o11y-ds-frontend/src/TraceToLogs/ @grafana/oss-big-tent @grafana/observability-traces-and-profiling /packages/grafana-o11y-ds-frontend/src/TraceToMetrics/ @grafana/oss-big-tent @grafana/observability-traces-and-profiling @@ -685,10 +686,9 @@ /packages/grafana-schema/src/**/gauge @grafana/dataviz-squad /packages/grafana-schema/src/**/geomap @grafana/dataviz-squad /packages/grafana-schema/src/**/googlecloudmonitoring @grafana/partner-datasources -/packages/grafana-schema/src/**/grafanapyroscope @grafana/observability-traces-and-profiling +/packages/grafana-schema/src/**/grafanapyroscope @grafana/oss-big-tent /packages/grafana-schema/src/**/heatmap @grafana/dataviz-squad /packages/grafana-schema/src/**/histogram @grafana/dataviz-squad -/packages/grafana-schema/src/**/librarypanel @grafana/sharing-squad /packages/grafana-schema/src/**/logs @grafana/observability-logs /packages/grafana-schema/src/**/logsnew @grafana/observability-logs /packages/grafana-schema/src/**/loki @grafana/oss-big-tent @grafana/observability-logs @@ -949,6 +949,7 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform /public/app/features/serviceaccounts/ @grafana/identity-squad /public/app/features/teams/ @grafana/access-squad /public/app/features/templating/ @grafana/dashboards-squad +/public/app/features/theme-playground/ @grafana/grafana-frontend-platform /public/app/features/trails/ @grafana/observability-metrics /public/app/features/transformers/ @grafana/datapro /public/app/features/transformers/timeSeriesTable/ @grafana/dataviz-squad @grafana/app-o11y-visualizations @@ -1089,8 +1090,8 @@ eslint-suppressions.json @grafanabot /public/app/plugins/datasource/prometheus/ @grafana/oss-big-tent /public/app/plugins/datasource/cloud-monitoring/ @grafana/partner-datasources /public/app/plugins/datasource/zipkin/ @grafana/oss-big-tent -/public/app/plugins/datasource/tempo/ @grafana/oss-big-tent @grafana/observability-traces-and-profiling -/public/app/plugins/datasource/grafana-pyroscope-datasource/ @grafana/observability-traces-and-profiling +/public/app/plugins/datasource/tempo/ @grafana/oss-big-tent +/public/app/plugins/datasource/grafana-pyroscope-datasource/ @grafana/oss-big-tent /public/app/plugins/datasource/parca/ @grafana/oss-big-tent /public/app/plugins/datasource/alertmanager/ @grafana/alerting-squad diff --git a/.github/commands.json b/.github/commands.json index 7b9c64b18e6..29106576726 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -144,7 +144,7 @@ "name": "datasource/grafana-pyroscope", "action": "addToProject", "addToProject": { - "url": "https://github.com/orgs/grafana/projects/221" + "url": "https://github.com/orgs/grafana/projects/457" } }, { diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml index 28cddc1e87a..cdce15360a4 100644 --- a/.github/workflows/go-lint.yml +++ b/.github/workflows/go-lint.yml @@ -68,7 +68,7 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd with: - version: v2.0.2 + version: v2.5.0 args: | --verbose $(go list -m -f '{{.Dir}}' | xargs -I{} sh -c 'test ! -f {}/.nolint && echo {}/...') install-mode: binary diff --git a/.gitignore b/.gitignore index d08dcd75bf1..036bb5dd2b1 100644 --- a/.gitignore +++ b/.gitignore @@ -253,5 +253,7 @@ public/mockServiceWorker.js # Ignore unified storage kv store files /grafana-kv-data - /codeowners-manifest/ + +# Ignore grafana/hippocampus local cache folder +.hippo diff --git a/Dockerfile b/Dockerfile index a5121873b10..77c825591f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ ARG JS_SRC=js-builder # By using FROM instructions we can delegate dependency updates to dependabot FROM alpine:3.21.3 AS alpine-base FROM ubuntu:22.04 AS ubuntu-base -FROM golang:1.24.6-alpine AS go-builder-base +FROM golang:1.25.3-alpine AS go-builder-base FROM --platform=${JS_PLATFORM} node:22-alpine AS js-builder-base # Javascript build stage FROM --platform=${JS_PLATFORM} ${JS_IMAGE} AS js-builder diff --git a/Makefile b/Makefile index e76e5a18940..c2ac23809c5 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ WIRE_TAGS = "oss" include .citools/Variables.mk GO = go -GO_VERSION = 1.24.6 +GO_VERSION = 1.25.3 GO_LINT_FILES ?= $(shell ./scripts/go-workspace/golangci-lint-includes.sh) GO_TEST_FILES ?= $(shell ./scripts/go-workspace/test-includes.sh) SH_FILES ?= $(shell find ./scripts -name *.sh) @@ -233,13 +233,13 @@ update-workspace: gen-go bash scripts/go-workspace/update-workspace.sh .PHONY: build-go -build-go: gen-go update-workspace ## Build all Go binaries. +build-go: ## Build all Go binaries. @echo "build go files with updated workspace" $(GO) run build.go $(GO_BUILD_FLAGS) build -build-go-fast: gen-go ## Build all Go binaries. - @echo "build go files" - $(GO) run build.go $(GO_BUILD_FLAGS) build +.PHONY: build-go-fast +build-go-fast: ## Build all Go binaries without updating workspace. + @echo "!!! [DEPRECATED] use build-go, they do the same thing now. This command will be removed soon" .PHONY: build-backend build-backend: ## Build Grafana backend. diff --git a/apps/advisor/go.mod b/apps/advisor/go.mod index 3f21822f5a4..29fd3c33281 100644 --- a/apps/advisor/go.mod +++ b/apps/advisor/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/advisor -go 1.24.6 +go 1.25.3 require ( github.com/Masterminds/semver/v3 v3.4.0 diff --git a/apps/alerting/alertenrichment/go.mod b/apps/alerting/alertenrichment/go.mod index 48c93f1aa1e..99e4446e23b 100644 --- a/apps/alerting/alertenrichment/go.mod +++ b/apps/alerting/alertenrichment/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/alerting/alertenrichment -go 1.24.6 +go 1.25.3 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -26,7 +26,7 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/text v0.29.0 // indirect google.golang.org/protobuf v1.36.9 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/alerting/alertenrichment/go.sum b/apps/alerting/alertenrichment/go.sum index ead26b4a7b0..96dc13e50a2 100644 --- a/apps/alerting/alertenrichment/go.sum +++ b/apps/alerting/alertenrichment/go.sum @@ -71,8 +71,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= 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= diff --git a/apps/alerting/notifications/go.mod b/apps/alerting/notifications/go.mod index f811c12d2f8..6907a28720a 100644 --- a/apps/alerting/notifications/go.mod +++ b/apps/alerting/notifications/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/alerting/notifications -go 1.24.6 +go 1.25.3 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -83,7 +83,7 @@ require ( go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/alerting/notifications/go.sum b/apps/alerting/notifications/go.sum index 9166be2eae7..b613313ed23 100644 --- a/apps/alerting/notifications/go.sum +++ b/apps/alerting/notifications/go.sum @@ -234,8 +234,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= diff --git a/apps/alerting/rules/go.mod b/apps/alerting/rules/go.mod index 7678f821f3a..c51dbdeeef3 100644 --- a/apps/alerting/rules/go.mod +++ b/apps/alerting/rules/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/alerting/rules -go 1.24.4 +go 1.25.3 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -66,7 +66,7 @@ require ( go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/alerting/rules/go.sum b/apps/alerting/rules/go.sum index dadd72070fd..ff48e3193e0 100644 --- a/apps/alerting/rules/go.sum +++ b/apps/alerting/rules/go.sum @@ -161,8 +161,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/correlations/go.mod b/apps/correlations/go.mod index 2f6538b879d..212391d0468 100644 --- a/apps/correlations/go.mod +++ b/apps/correlations/go.mod @@ -1,8 +1,6 @@ module github.com/grafana/grafana/apps/correlations -go 1.24.0 - -toolchain go1.24.6 +go 1.25.3 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -68,7 +66,7 @@ require ( go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/correlations/go.sum b/apps/correlations/go.sum index dadd72070fd..ff48e3193e0 100644 --- a/apps/correlations/go.sum +++ b/apps/correlations/go.sum @@ -161,8 +161,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/dashboard/go.mod b/apps/dashboard/go.mod index adfae61e8d1..69ce9f376f8 100644 --- a/apps/dashboard/go.mod +++ b/apps/dashboard/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/dashboard -go 1.24.6 +go 1.25.3 require ( cuelang.org/go v0.11.1 @@ -11,7 +11,7 @@ require ( github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e github.com/prometheus/client_golang v1.23.2 github.com/stretchr/testify v1.11.1 - golang.org/x/net v0.44.0 + golang.org/x/net v0.45.0 k8s.io/apimachinery v0.34.1 k8s.io/apiserver v0.34.1 k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b diff --git a/apps/dashboard/go.sum b/apps/dashboard/go.sum index 4358d7d815a..9f18f73b002 100644 --- a/apps/dashboard/go.sum +++ b/apps/dashboard/go.sum @@ -334,8 +334,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/dashboard/kinds/dashboard.cue b/apps/dashboard/kinds/dashboard.cue index 905b9606336..e8dfea3bf98 100644 --- a/apps/dashboard/kinds/dashboard.cue +++ b/apps/dashboard/kinds/dashboard.cue @@ -27,6 +27,9 @@ ConversionStatus: { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. storedVersion?: string + + // The original value map[string]any + source?: _ } dashboard: { diff --git a/apps/dashboard/kinds/v2beta1/dashboard_spec.cue b/apps/dashboard/kinds/v2beta1/dashboard_spec.cue index fe9d13de67f..f0278fc431c 100644 --- a/apps/dashboard/kinds/v2beta1/dashboard_spec.cue +++ b/apps/dashboard/kinds/v2beta1/dashboard_spec.cue @@ -710,9 +710,9 @@ VariableCustomFormatterFn: { // `custom`: Define the variable options manually using a comma-separated list. // `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" | - "system" | "snapshot" + "system" | "snapshot" | "switch" -VariableKind: QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind +VariableKind: QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind | SwitchVariableKind // Sort variable options // Accepted values are: @@ -904,6 +904,22 @@ CustomVariableKind: { spec: CustomVariableSpec } +SwitchVariableSpec: { + name: string | *"" + current: string | *"false" + enabledValue: string | *"true" + disabledValue: string | *"false" + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +SwitchVariableKind: { + kind: "SwitchVariable" + spec: SwitchVariableSpec +} + // GroupBy variable specification GroupByVariableSpec: { name: string | *"" diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_kind.cue b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_kind.cue index fb971b2cc3f..526ddba3bfc 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_kind.cue +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_kind.cue @@ -364,8 +364,9 @@ lineage: schemas: [{ // `textbox`: Display a free text input field with an optional default value. // `custom`: Define the variable options manually using a comma-separated list. // `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables + // `switch`: Boolean variables rendered as a switch #VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" | - "system" | "snapshot" @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview) + "system" | "snapshot" | "switch" @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview) // Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. // Continuous color interpolates a color using the percentage of a value relative to min and max. diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go index 2dec6aea951..1db6c8e3467 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/dashboard_status_gen.go @@ -15,6 +15,8 @@ type DashboardConversionStatus struct { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. StoredVersion *string `json:"storedVersion,omitempty"` + // The original value map[string]any + Source interface{} `json:"source,omitempty"` } // NewDashboardConversionStatus creates a new DashboardConversionStatus object. diff --git a/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go index 0027a8b8cc4..63af5e26b7e 100644 --- a/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v0alpha1/zz_generated.openapi.go @@ -272,6 +272,13 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardConversionStatus(ref common.Ref Format: "", }, }, + "source": { + SchemaProps: spec.SchemaProps{ + Description: "The original value map[string]any", + Type: []string{"object"}, + Format: "", + }, + }, }, Required: []string{"failed"}, }, diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_kind.cue b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_kind.cue index fb971b2cc3f..526ddba3bfc 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_kind.cue +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_kind.cue @@ -364,8 +364,9 @@ lineage: schemas: [{ // `textbox`: Display a free text input field with an optional default value. // `custom`: Define the variable options manually using a comma-separated list. // `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables + // `switch`: Boolean variables rendered as a switch #VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" | - "system" | "snapshot" @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview) + "system" | "snapshot" | "switch" @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview) // Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. // Continuous color interpolates a color using the percentage of a value relative to min and max. diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_status_gen.go b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_status_gen.go index a2ac9850b09..5fa18c8f821 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_status_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/dashboard_status_gen.go @@ -15,6 +15,8 @@ type DashboardConversionStatus struct { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. StoredVersion *string `json:"storedVersion,omitempty"` + // The original value map[string]any + Source interface{} `json:"source,omitempty"` } // NewDashboardConversionStatus creates a new DashboardConversionStatus object. diff --git a/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go index ab68539f1fe..be747be2ae3 100644 --- a/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v1beta1/zz_generated.openapi.go @@ -264,6 +264,13 @@ func schema_pkg_apis_dashboard_v1beta1_DashboardConversionStatus(ref common.Refe Format: "", }, }, + "source": { + SchemaProps: spec.SchemaProps{ + Description: "The original value map[string]any", + Type: []string{"object"}, + Format: "", + }, + }, }, Required: []string{"failed"}, }, diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go index 9544b902dd8..6fad54db0b4 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/dashboard_status_gen.go @@ -15,6 +15,8 @@ type DashboardConversionStatus struct { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. StoredVersion *string `json:"storedVersion,omitempty"` + // The original value map[string]any + Source interface{} `json:"source,omitempty"` } // NewDashboardConversionStatus creates a new DashboardConversionStatus object. diff --git a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go index 1c030f705c9..548a8c93268 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v2alpha1/zz_generated.openapi.go @@ -1353,6 +1353,13 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardConversionStatus(ref common.Ref Format: "", }, }, + "source": { + SchemaProps: spec.SchemaProps{ + Description: "The original value map[string]any", + Type: []string{"object"}, + Format: "", + }, + }, }, Required: []string{"failed"}, }, diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue index 2c7d74674f2..fb46afb5c76 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec.cue @@ -714,9 +714,9 @@ VariableCustomFormatterFn: { // `custom`: Define the variable options manually using a comma-separated list. // `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" | - "system" | "snapshot" + "system" | "snapshot" | "switch" -VariableKind: QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind +VariableKind: QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind | SwitchVariableKind // Sort variable options // Accepted values are: @@ -908,6 +908,22 @@ CustomVariableKind: { spec: CustomVariableSpec } +SwitchVariableSpec: { + name: string | *"" + current: string | *"false" + enabledValue: string | *"true" + disabledValue: string | *"false" + label?: string + hide: VariableHide + skipUrlSync: bool | *false + description?: string +} + +SwitchVariableKind: { + kind: "SwitchVariable" + spec: SwitchVariableSpec +} + // GroupBy variable specification GroupByVariableSpec: { name: string | *"" diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go index d43c7765f16..783d91665a0 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_spec_gen.go @@ -1290,11 +1290,11 @@ func NewDashboardTimeRangeOption() *DashboardTimeRangeOption { } // +k8s:openapi-gen=true -type DashboardVariableKind = DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind +type DashboardVariableKind = DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind // NewDashboardVariableKind creates a new DashboardVariableKind object. func NewDashboardVariableKind() *DashboardVariableKind { - return NewDashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind() + return NewDashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind() } // Query variable kind @@ -1815,6 +1815,44 @@ func NewDashboardMetricFindValue() *DashboardMetricFindValue { return &DashboardMetricFindValue{} } +// +k8s:openapi-gen=true +type DashboardSwitchVariableKind struct { + Kind string `json:"kind"` + Spec DashboardSwitchVariableSpec `json:"spec"` +} + +// NewDashboardSwitchVariableKind creates a new DashboardSwitchVariableKind object. +func NewDashboardSwitchVariableKind() *DashboardSwitchVariableKind { + return &DashboardSwitchVariableKind{ + Kind: "SwitchVariable", + Spec: *NewDashboardSwitchVariableSpec(), + } +} + +// +k8s:openapi-gen=true +type DashboardSwitchVariableSpec struct { + Name string `json:"name"` + Current string `json:"current"` + EnabledValue string `json:"enabledValue"` + DisabledValue string `json:"disabledValue"` + Label *string `json:"label,omitempty"` + Hide DashboardVariableHide `json:"hide"` + SkipUrlSync bool `json:"skipUrlSync"` + Description *string `json:"description,omitempty"` +} + +// NewDashboardSwitchVariableSpec creates a new DashboardSwitchVariableSpec object. +func NewDashboardSwitchVariableSpec() *DashboardSwitchVariableSpec { + return &DashboardSwitchVariableSpec{ + Name: "", + Current: "false", + EnabledValue: "true", + DisabledValue: "false", + Hide: DashboardVariableHideDontHide, + SkipUrlSync: false, + } +} + // +k8s:openapi-gen=true type DashboardSpec struct { Annotations []DashboardAnnotationQueryKind `json:"annotations"` @@ -2433,7 +2471,7 @@ func (resource *DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTab } // +k8s:openapi-gen=true -type DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind struct { +type DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind struct { QueryVariableKind *DashboardQueryVariableKind `json:"QueryVariableKind,omitempty"` TextVariableKind *DashboardTextVariableKind `json:"TextVariableKind,omitempty"` ConstantVariableKind *DashboardConstantVariableKind `json:"ConstantVariableKind,omitempty"` @@ -2442,15 +2480,16 @@ type DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasou CustomVariableKind *DashboardCustomVariableKind `json:"CustomVariableKind,omitempty"` GroupByVariableKind *DashboardGroupByVariableKind `json:"GroupByVariableKind,omitempty"` AdhocVariableKind *DashboardAdhocVariableKind `json:"AdhocVariableKind,omitempty"` + SwitchVariableKind *DashboardSwitchVariableKind `json:"SwitchVariableKind,omitempty"` } -// NewDashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind creates a new DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind object. -func NewDashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind() *DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind { - return &DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind{} +// NewDashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind creates a new DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind object. +func NewDashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind() *DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind { + return &DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind{} } -// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind` as JSON. -func (resource DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind) MarshalJSON() ([]byte, error) { +// MarshalJSON implements a custom JSON marshalling logic to encode `DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind` as JSON. +func (resource DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind) MarshalJSON() ([]byte, error) { if resource.QueryVariableKind != nil { return json.Marshal(resource.QueryVariableKind) } @@ -2475,12 +2514,15 @@ func (resource DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKin if resource.AdhocVariableKind != nil { return json.Marshal(resource.AdhocVariableKind) } + if resource.SwitchVariableKind != nil { + return json.Marshal(resource.SwitchVariableKind) + } return []byte("null"), nil } -// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind` from JSON. -func (resource *DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind) UnmarshalJSON(raw []byte) error { +// UnmarshalJSON implements a custom JSON unmarshalling logic to decode `DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind` from JSON. +func (resource *DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind) UnmarshalJSON(raw []byte) error { if raw == nil { return nil } @@ -2553,6 +2595,14 @@ func (resource *DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKi resource.QueryVariableKind = &dashboardQueryVariableKind return nil + case "SwitchVariable": + var dashboardSwitchVariableKind DashboardSwitchVariableKind + if err := json.Unmarshal(raw, &dashboardSwitchVariableKind); err != nil { + return err + } + + resource.SwitchVariableKind = &dashboardSwitchVariableKind + return nil case "TextVariable": var dashboardTextVariableKind DashboardTextVariableKind if err := json.Unmarshal(raw, &dashboardTextVariableKind); err != nil { diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_status_gen.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_status_gen.go index 62d39eb766d..a6bd3ee5e18 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_status_gen.go +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/dashboard_status_gen.go @@ -15,6 +15,8 @@ type DashboardConversionStatus struct { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. StoredVersion *string `json:"storedVersion,omitempty"` + // The original value map[string]any + Source interface{} `json:"source,omitempty"` } // NewDashboardConversionStatus creates a new DashboardConversionStatus object. diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go index d1429c9ae7f..5e51134e80c 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi.go @@ -14,127 +14,129 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.AnnotationActions": schema_pkg_apis_dashboard_v2beta1_AnnotationActions(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.AnnotationPermission": schema_pkg_apis_dashboard_v2beta1_AnnotationPermission(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.Dashboard": schema_pkg_apis_dashboard_v2beta1_Dashboard(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAccess": schema_pkg_apis_dashboard_v2beta1_DashboardAccess(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAction": schema_pkg_apis_dashboard_v2beta1_DashboardAction(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardActionVariable": schema_pkg_apis_dashboard_v2beta1_DashboardActionVariable(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdHocFilterWithLabels": schema_pkg_apis_dashboard_v2beta1_DashboardAdHocFilterWithLabels(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdhocVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardAdhocVariableKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdhocVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardAdhocVariableSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAnnotationPanelFilter": schema_pkg_apis_dashboard_v2beta1_DashboardAnnotationPanelFilter(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAnnotationQueryKind": schema_pkg_apis_dashboard_v2beta1_DashboardAnnotationQueryKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAnnotationQuerySpec": schema_pkg_apis_dashboard_v2beta1_DashboardAnnotationQuerySpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAutoGridLayoutItemKind": schema_pkg_apis_dashboard_v2beta1_DashboardAutoGridLayoutItemKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAutoGridLayoutItemSpec": schema_pkg_apis_dashboard_v2beta1_DashboardAutoGridLayoutItemSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAutoGridLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardAutoGridLayoutKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAutoGridLayoutSpec": schema_pkg_apis_dashboard_v2beta1_DashboardAutoGridLayoutSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAutoGridRepeatOptions": schema_pkg_apis_dashboard_v2beta1_DashboardAutoGridRepeatOptions(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardClient": schema_pkg_apis_dashboard_v2beta1_DashboardClient(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingDataKind": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingDataKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingDataSpec": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingDataSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingGroupKind": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingGroupKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingGroupSpec": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingGroupSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingTimeRangeSizeKind": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingTimeRangeSizeKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingTimeRangeSizeSpec": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingTimeRangeSizeSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingVariableKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingVariableSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConstantVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardConstantVariableKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConstantVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardConstantVariableSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConversionStatus": schema_pkg_apis_dashboard_v2beta1_DashboardConversionStatus(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardCustomVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardCustomVariableKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardCustomVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardCustomVariableSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDashboardLink": schema_pkg_apis_dashboard_v2beta1_DashboardDashboardLink(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDataLink": schema_pkg_apis_dashboard_v2beta1_DashboardDataLink(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDataQueryKind": schema_pkg_apis_dashboard_v2beta1_DashboardDataQueryKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDataTransformerConfig": schema_pkg_apis_dashboard_v2beta1_DashboardDataTransformerConfig(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDatasourceVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardDatasourceVariableKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDatasourceVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardDatasourceVariableSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDynamicConfigValue": schema_pkg_apis_dashboard_v2beta1_DashboardDynamicConfigValue(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardElementReference": schema_pkg_apis_dashboard_v2beta1_DashboardElementReference(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFetchOptions": schema_pkg_apis_dashboard_v2beta1_DashboardFetchOptions(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldColor": schema_pkg_apis_dashboard_v2beta1_DashboardFieldColor(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldConfig": schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfig(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldConfigSource": schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfigSource(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutItemKind": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutItemKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutItemSpec": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutItemSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutSpec": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGroupByVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardGroupByVariableKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGroupByVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardGroupByVariableSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardInfinityOptions": schema_pkg_apis_dashboard_v2beta1_DashboardInfinityOptions(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardIntervalVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardIntervalVariableKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardIntervalVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardIntervalVariableSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardJSONCodec": schema_pkg_apis_dashboard_v2beta1_DashboardJSONCodec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardLibraryPanelKind": schema_pkg_apis_dashboard_v2beta1_DashboardLibraryPanelKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardLibraryPanelKindSpec": schema_pkg_apis_dashboard_v2beta1_DashboardLibraryPanelKindSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardLibraryPanelRef": schema_pkg_apis_dashboard_v2beta1_DashboardLibraryPanelRef(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardList": schema_pkg_apis_dashboard_v2beta1_DashboardList(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardMatcherConfig": schema_pkg_apis_dashboard_v2beta1_DashboardMatcherConfig(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardMetadata": schema_pkg_apis_dashboard_v2beta1_DashboardMetadata(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardMetricFindValue": schema_pkg_apis_dashboard_v2beta1_DashboardMetricFindValue(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelKind": schema_pkg_apis_dashboard_v2beta1_DashboardPanelKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelKindOrLibraryPanelKind": schema_pkg_apis_dashboard_v2beta1_DashboardPanelKindOrLibraryPanelKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelQueryKind": schema_pkg_apis_dashboard_v2beta1_DashboardPanelQueryKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelQuerySpec": schema_pkg_apis_dashboard_v2beta1_DashboardPanelQuerySpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelSpec": schema_pkg_apis_dashboard_v2beta1_DashboardPanelSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryGroupKind": schema_pkg_apis_dashboard_v2beta1_DashboardQueryGroupKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryGroupSpec": schema_pkg_apis_dashboard_v2beta1_DashboardQueryGroupSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryOptionsSpec": schema_pkg_apis_dashboard_v2beta1_DashboardQueryOptionsSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRangeMap": schema_pkg_apis_dashboard_v2beta1_DashboardRangeMap(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRegexMap": schema_pkg_apis_dashboard_v2beta1_DashboardRegexMap(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRepeatOptions": schema_pkg_apis_dashboard_v2beta1_DashboardRepeatOptions(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRowRepeatOptions": schema_pkg_apis_dashboard_v2beta1_DashboardRowRepeatOptions(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRowsLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardRowsLayoutKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRowsLayoutRowKind": schema_pkg_apis_dashboard_v2beta1_DashboardRowsLayoutRowKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRowsLayoutRowSpec": schema_pkg_apis_dashboard_v2beta1_DashboardRowsLayoutRowSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRowsLayoutSpec": schema_pkg_apis_dashboard_v2beta1_DashboardRowsLayoutSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardSpec": schema_pkg_apis_dashboard_v2beta1_DashboardSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardSpecialValueMap": schema_pkg_apis_dashboard_v2beta1_DashboardSpecialValueMap(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardStatus": schema_pkg_apis_dashboard_v2beta1_DashboardStatus(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardStringOrArrayOfString": schema_pkg_apis_dashboard_v2beta1_DashboardStringOrArrayOfString(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardStringOrFloat64": schema_pkg_apis_dashboard_v2beta1_DashboardStringOrFloat64(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTabRepeatOptions": schema_pkg_apis_dashboard_v2beta1_DashboardTabRepeatOptions(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTabsLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardTabsLayoutKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTabsLayoutSpec": schema_pkg_apis_dashboard_v2beta1_DashboardTabsLayoutSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTabsLayoutTabKind": schema_pkg_apis_dashboard_v2beta1_DashboardTabsLayoutTabKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTabsLayoutTabSpec": schema_pkg_apis_dashboard_v2beta1_DashboardTabsLayoutTabSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTextVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardTextVariableKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTextVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardTextVariableSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardThreshold": schema_pkg_apis_dashboard_v2beta1_DashboardThreshold(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardThresholdsConfig": schema_pkg_apis_dashboard_v2beta1_DashboardThresholdsConfig(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTimeRangeOption": schema_pkg_apis_dashboard_v2beta1_DashboardTimeRangeOption(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTimeSettingsSpec": schema_pkg_apis_dashboard_v2beta1_DashboardTimeSettingsSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTransformationKind": schema_pkg_apis_dashboard_v2beta1_DashboardTransformationKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1ActionStyle": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1ActionStyle(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1AdhocVariableKindDatasource": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1AdhocVariableKindDatasource(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1DataQueryKindDatasource": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1DataQueryKindDatasource(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1FieldConfigSourceOverrides": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1FieldConfigSourceOverrides(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1GroupByVariableKindDatasource": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1GroupByVariableKindDatasource(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1RangeMapOptions": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1RangeMapOptions(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1RegexMapOptions": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1RegexMapOptions(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1SpecialValueMapOptions": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1SpecialValueMapOptions(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardValueMap": schema_pkg_apis_dashboard_v2beta1_DashboardValueMap(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap": schema_pkg_apis_dashboard_v2beta1_DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardValueMappingResult": schema_pkg_apis_dashboard_v2beta1_DashboardValueMappingResult(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardVariableOption": schema_pkg_apis_dashboard_v2beta1_DashboardVariableOption(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardVersionInfo": schema_pkg_apis_dashboard_v2beta1_DashboardVersionInfo(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardVersionList": schema_pkg_apis_dashboard_v2beta1_DashboardVersionList(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardVizConfigKind": schema_pkg_apis_dashboard_v2beta1_DashboardVizConfigKind(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardVizConfigSpec": schema_pkg_apis_dashboard_v2beta1_DashboardVizConfigSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardWithAccessInfo": schema_pkg_apis_dashboard_v2beta1_DashboardWithAccessInfo(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.LibraryPanel": schema_pkg_apis_dashboard_v2beta1_LibraryPanel(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.LibraryPanelList": schema_pkg_apis_dashboard_v2beta1_LibraryPanelList(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.LibraryPanelSpec": schema_pkg_apis_dashboard_v2beta1_LibraryPanelSpec(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.LibraryPanelStatus": schema_pkg_apis_dashboard_v2beta1_LibraryPanelStatus(ref), - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.VersionsQueryOptions": schema_pkg_apis_dashboard_v2beta1_VersionsQueryOptions(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.AnnotationActions": schema_pkg_apis_dashboard_v2beta1_AnnotationActions(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.AnnotationPermission": schema_pkg_apis_dashboard_v2beta1_AnnotationPermission(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.Dashboard": schema_pkg_apis_dashboard_v2beta1_Dashboard(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAccess": schema_pkg_apis_dashboard_v2beta1_DashboardAccess(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAction": schema_pkg_apis_dashboard_v2beta1_DashboardAction(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardActionVariable": schema_pkg_apis_dashboard_v2beta1_DashboardActionVariable(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdHocFilterWithLabels": schema_pkg_apis_dashboard_v2beta1_DashboardAdHocFilterWithLabels(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdhocVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardAdhocVariableKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdhocVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardAdhocVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAnnotationPanelFilter": schema_pkg_apis_dashboard_v2beta1_DashboardAnnotationPanelFilter(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAnnotationQueryKind": schema_pkg_apis_dashboard_v2beta1_DashboardAnnotationQueryKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAnnotationQuerySpec": schema_pkg_apis_dashboard_v2beta1_DashboardAnnotationQuerySpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAutoGridLayoutItemKind": schema_pkg_apis_dashboard_v2beta1_DashboardAutoGridLayoutItemKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAutoGridLayoutItemSpec": schema_pkg_apis_dashboard_v2beta1_DashboardAutoGridLayoutItemSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAutoGridLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardAutoGridLayoutKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAutoGridLayoutSpec": schema_pkg_apis_dashboard_v2beta1_DashboardAutoGridLayoutSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAutoGridRepeatOptions": schema_pkg_apis_dashboard_v2beta1_DashboardAutoGridRepeatOptions(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardClient": schema_pkg_apis_dashboard_v2beta1_DashboardClient(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingDataKind": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingDataKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingDataSpec": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingDataSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingGroupKind": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingGroupKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingGroupSpec": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingGroupSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingTimeRangeSizeKind": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingTimeRangeSizeKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingTimeRangeSizeSpec": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingTimeRangeSizeSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingVariableKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConditionalRenderingVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardConditionalRenderingVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConstantVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardConstantVariableKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConstantVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardConstantVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConversionStatus": schema_pkg_apis_dashboard_v2beta1_DashboardConversionStatus(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardCustomVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardCustomVariableKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardCustomVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardCustomVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDashboardLink": schema_pkg_apis_dashboard_v2beta1_DashboardDashboardLink(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDataLink": schema_pkg_apis_dashboard_v2beta1_DashboardDataLink(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDataQueryKind": schema_pkg_apis_dashboard_v2beta1_DashboardDataQueryKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDataTransformerConfig": schema_pkg_apis_dashboard_v2beta1_DashboardDataTransformerConfig(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDatasourceVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardDatasourceVariableKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDatasourceVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardDatasourceVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDynamicConfigValue": schema_pkg_apis_dashboard_v2beta1_DashboardDynamicConfigValue(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardElementReference": schema_pkg_apis_dashboard_v2beta1_DashboardElementReference(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFetchOptions": schema_pkg_apis_dashboard_v2beta1_DashboardFetchOptions(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldColor": schema_pkg_apis_dashboard_v2beta1_DashboardFieldColor(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldConfig": schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfig(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardFieldConfigSource": schema_pkg_apis_dashboard_v2beta1_DashboardFieldConfigSource(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutItemKind": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutItemKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutItemSpec": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutItemSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutSpec": schema_pkg_apis_dashboard_v2beta1_DashboardGridLayoutSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGroupByVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardGroupByVariableKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGroupByVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardGroupByVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardInfinityOptions": schema_pkg_apis_dashboard_v2beta1_DashboardInfinityOptions(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardIntervalVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardIntervalVariableKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardIntervalVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardIntervalVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardJSONCodec": schema_pkg_apis_dashboard_v2beta1_DashboardJSONCodec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardLibraryPanelKind": schema_pkg_apis_dashboard_v2beta1_DashboardLibraryPanelKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardLibraryPanelKindSpec": schema_pkg_apis_dashboard_v2beta1_DashboardLibraryPanelKindSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardLibraryPanelRef": schema_pkg_apis_dashboard_v2beta1_DashboardLibraryPanelRef(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardList": schema_pkg_apis_dashboard_v2beta1_DashboardList(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardMatcherConfig": schema_pkg_apis_dashboard_v2beta1_DashboardMatcherConfig(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardMetadata": schema_pkg_apis_dashboard_v2beta1_DashboardMetadata(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardMetricFindValue": schema_pkg_apis_dashboard_v2beta1_DashboardMetricFindValue(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelKind": schema_pkg_apis_dashboard_v2beta1_DashboardPanelKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelKindOrLibraryPanelKind": schema_pkg_apis_dashboard_v2beta1_DashboardPanelKindOrLibraryPanelKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelQueryKind": schema_pkg_apis_dashboard_v2beta1_DashboardPanelQueryKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelQuerySpec": schema_pkg_apis_dashboard_v2beta1_DashboardPanelQuerySpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelSpec": schema_pkg_apis_dashboard_v2beta1_DashboardPanelSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryGroupKind": schema_pkg_apis_dashboard_v2beta1_DashboardQueryGroupKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryGroupSpec": schema_pkg_apis_dashboard_v2beta1_DashboardQueryGroupSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryOptionsSpec": schema_pkg_apis_dashboard_v2beta1_DashboardQueryOptionsSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRangeMap": schema_pkg_apis_dashboard_v2beta1_DashboardRangeMap(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRegexMap": schema_pkg_apis_dashboard_v2beta1_DashboardRegexMap(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRepeatOptions": schema_pkg_apis_dashboard_v2beta1_DashboardRepeatOptions(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRowRepeatOptions": schema_pkg_apis_dashboard_v2beta1_DashboardRowRepeatOptions(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRowsLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardRowsLayoutKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRowsLayoutRowKind": schema_pkg_apis_dashboard_v2beta1_DashboardRowsLayoutRowKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRowsLayoutRowSpec": schema_pkg_apis_dashboard_v2beta1_DashboardRowsLayoutRowSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardRowsLayoutSpec": schema_pkg_apis_dashboard_v2beta1_DashboardRowsLayoutSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardSpec": schema_pkg_apis_dashboard_v2beta1_DashboardSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardSpecialValueMap": schema_pkg_apis_dashboard_v2beta1_DashboardSpecialValueMap(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardStatus": schema_pkg_apis_dashboard_v2beta1_DashboardStatus(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardStringOrArrayOfString": schema_pkg_apis_dashboard_v2beta1_DashboardStringOrArrayOfString(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardStringOrFloat64": schema_pkg_apis_dashboard_v2beta1_DashboardStringOrFloat64(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardSwitchVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardSwitchVariableKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardSwitchVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardSwitchVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTabRepeatOptions": schema_pkg_apis_dashboard_v2beta1_DashboardTabRepeatOptions(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTabsLayoutKind": schema_pkg_apis_dashboard_v2beta1_DashboardTabsLayoutKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTabsLayoutSpec": schema_pkg_apis_dashboard_v2beta1_DashboardTabsLayoutSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTabsLayoutTabKind": schema_pkg_apis_dashboard_v2beta1_DashboardTabsLayoutTabKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTabsLayoutTabSpec": schema_pkg_apis_dashboard_v2beta1_DashboardTabsLayoutTabSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTextVariableKind": schema_pkg_apis_dashboard_v2beta1_DashboardTextVariableKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTextVariableSpec": schema_pkg_apis_dashboard_v2beta1_DashboardTextVariableSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardThreshold": schema_pkg_apis_dashboard_v2beta1_DashboardThreshold(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardThresholdsConfig": schema_pkg_apis_dashboard_v2beta1_DashboardThresholdsConfig(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTimeRangeOption": schema_pkg_apis_dashboard_v2beta1_DashboardTimeRangeOption(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTimeSettingsSpec": schema_pkg_apis_dashboard_v2beta1_DashboardTimeSettingsSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTransformationKind": schema_pkg_apis_dashboard_v2beta1_DashboardTransformationKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1ActionStyle": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1ActionStyle(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1AdhocVariableKindDatasource": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1AdhocVariableKindDatasource(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1DataQueryKindDatasource": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1DataQueryKindDatasource(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1FieldConfigSourceOverrides": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1FieldConfigSourceOverrides(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1GroupByVariableKindDatasource": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1GroupByVariableKindDatasource(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1RangeMapOptions": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1RangeMapOptions(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1RegexMapOptions": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1RegexMapOptions(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardV2beta1SpecialValueMapOptions": schema_pkg_apis_dashboard_v2beta1_DashboardV2beta1SpecialValueMapOptions(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardValueMap": schema_pkg_apis_dashboard_v2beta1_DashboardValueMap(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap": schema_pkg_apis_dashboard_v2beta1_DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardValueMappingResult": schema_pkg_apis_dashboard_v2beta1_DashboardValueMappingResult(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardVariableOption": schema_pkg_apis_dashboard_v2beta1_DashboardVariableOption(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardVersionInfo": schema_pkg_apis_dashboard_v2beta1_DashboardVersionInfo(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardVersionList": schema_pkg_apis_dashboard_v2beta1_DashboardVersionList(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardVizConfigKind": schema_pkg_apis_dashboard_v2beta1_DashboardVizConfigKind(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardVizConfigSpec": schema_pkg_apis_dashboard_v2beta1_DashboardVizConfigSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardWithAccessInfo": schema_pkg_apis_dashboard_v2beta1_DashboardWithAccessInfo(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.LibraryPanel": schema_pkg_apis_dashboard_v2beta1_LibraryPanel(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.LibraryPanelList": schema_pkg_apis_dashboard_v2beta1_LibraryPanelList(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.LibraryPanelSpec": schema_pkg_apis_dashboard_v2beta1_LibraryPanelSpec(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.LibraryPanelStatus": schema_pkg_apis_dashboard_v2beta1_LibraryPanelStatus(ref), + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.VersionsQueryOptions": schema_pkg_apis_dashboard_v2beta1_VersionsQueryOptions(ref), } } @@ -1358,6 +1360,13 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardConversionStatus(ref common.Refe Format: "", }, }, + "source": { + SchemaProps: spec.SchemaProps{ + Description: "The original value map[string]any", + Type: []string{"object"}, + Format: "", + }, + }, }, Required: []string{"failed"}, }, @@ -3452,7 +3461,7 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableKind(ref common.Ref } } -func schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { +func schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -3498,11 +3507,16 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardQueryVariableKindOrTextVariableK Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdhocVariableKind"), }, }, + "SwitchVariableKind": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardSwitchVariableKind"), + }, + }, }, }, }, Dependencies: []string{ - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdhocVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConstantVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardCustomVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDatasourceVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGroupByVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardIntervalVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTextVariableKind"}, + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAdhocVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardConstantVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardCustomVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDatasourceVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGroupByVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardIntervalVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardSwitchVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTextVariableKind"}, } } @@ -4054,7 +4068,7 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardSpec(ref common.ReferenceCallbac Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ - Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind"), + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind"), }, }, }, @@ -4065,7 +4079,7 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardSpec(ref common.ReferenceCallbac }, }, Dependencies: []string{ - "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAnnotationQueryKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDashboardLink", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelKindOrLibraryPanelKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTimeSettingsSpec"}, + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardAnnotationQueryKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardDashboardLink", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardPanelKindOrLibraryPanelKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind", "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardTimeSettingsSpec"}, } } @@ -4174,6 +4188,101 @@ func schema_pkg_apis_dashboard_v2beta1_DashboardStringOrFloat64(ref common.Refer } } +func schema_pkg_apis_dashboard_v2beta1_DashboardSwitchVariableKind(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardSwitchVariableSpec"), + }, + }, + }, + Required: []string{"kind", "spec"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1.DashboardSwitchVariableSpec"}, + } +} + +func schema_pkg_apis_dashboard_v2beta1_DashboardSwitchVariableSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "current": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "enabledValue": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "disabledValue": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "label": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "hide": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "skipUrlSync": { + SchemaProps: spec.SchemaProps{ + Default: false, + Type: []string{"boolean"}, + Format: "", + }, + }, + "description": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "current", "enabledValue", "disabledValue", "hide", "skipUrlSync"}, + }, + }, + } +} + func schema_pkg_apis_dashboard_v2beta1_DashboardTabRepeatOptions(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi_violation_exceptions.list b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi_violation_exceptions.list index b6c9fc53fd2..5a0edf5747d 100644 --- a/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi_violation_exceptions.list +++ b/apps/dashboard/pkg/apis/dashboard/v2beta1/zz_generated.openapi_violation_exceptions.list @@ -55,14 +55,15 @@ API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/ap API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardIntervalVariableSpec,AutoMin API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardPanelKindOrLibraryPanelKind,LibraryPanelKind API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardPanelKindOrLibraryPanelKind,PanelKind -API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,AdhocVariableKind -API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,ConstantVariableKind -API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,CustomVariableKind -API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,DatasourceVariableKind -API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,GroupByVariableKind -API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,IntervalVariableKind -API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,QueryVariableKind -API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,TextVariableKind +API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind,AdhocVariableKind +API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind,ConstantVariableKind +API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind,CustomVariableKind +API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind,DatasourceVariableKind +API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind,GroupByVariableKind +API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind,IntervalVariableKind +API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind,QueryVariableKind +API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind,SwitchVariableKind +API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKindOrSwitchVariableKind,TextVariableKind API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardStringOrArrayOfString,ArrayOfString API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardStringOrArrayOfString,String API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2beta1,DashboardStringOrFloat64,Float64 diff --git a/apps/dashboard/pkg/migration/README.md b/apps/dashboard/pkg/migration/README.md index 489659ec23e..4dffadf42bb 100644 --- a/apps/dashboard/pkg/migration/README.md +++ b/apps/dashboard/pkg/migration/README.md @@ -1,10 +1,144 @@ # Dashboard migrations -This document describes the Grafana dashboard migration system, including metrics, logging, and testing infrastructure for dashboard schema migrations and API version conversions. +This document describes the Grafana dashboard migration system, focusing on conversion-level practices including metrics, logging, and testing infrastructure for API version conversions. For schema version migration implementation details, see the [SchemaVersion Migration Guide](./schemaversion/README.md). + +## Table of Contents + +- [Overview](#overview) + - [Conversion Flow](#conversion-flow-v0--v1--v2) + - [v0 to v1 Conversion](#v0-to-v1-conversion) + - [v1 to v2 Conversion](#v1-to-v2-conversion) +- [Conversion Matrix](#conversion-matrix) +- [API Versions](#api-versions) +- [Schema Versions](#schema-versions) +- [Testing](#testing) + - [Backend conversion tests](#backend-conversion-tests) + - [Frontend migration comparison tests](#frontend-migration-comparison-tests) +- [Monitoring Migrations](#monitoring-migrations) + - [Metrics](#metrics) + - [Dashboard conversion success metric](#1-dashboard-conversion-success-metric) + - [Dashboard conversion failure metric](#2-dashboard-conversion-failure-metric) + - [Error Types](#error-types) + - [Logging](#logging) + - [Log structure](#log-structure) + - [Log levels](#log-levels) + - [Implementation Details](#implementation-details) + - [Automatic instrumentation](#automatic-instrumentation) + - [Error handling](#error-handling) + - [Registration](#registration) + - [Metrics registration](#metrics-registration) + - [Available metrics](#available-metrics) +- [Related Documentation](#related-documentation) ## Overview -## Metrics +The Grafana dashboard migration system operates across three main conversion layers: + +### Conversion Flow: v0 → v1 → v2 + +``` +v0alpha1 (Legacy JSON) → v1beta1 (Migrated JSON) → v2alpha1/v2beta1 (Structured) +``` + +#### v0 to v1 Conversion: +- All schema migrations (v0-v42) are executed in the backend +- Ports the logic from DashboardMigrator and implements built-in plugin migrations for panel plugins since backend cannot load plugins +- Transforms legacy JSON dashboards to migrated JSON format +- Handles backward compatibility for older dashboard formats +- See [SchemaVersion Migration Guide](./schemaversion/README.md) for detailed instructions on creating new schema migrations + +#### v1 to v2 Conversion: +- API version conversions between different Kubernetes API versions +- Transforms JSON dashboards to structured dashboard format +- v2 schema is the stable, typed schema with proper type definitions +- Handles modern dashboard features and Kubernetes-native storage + +## Conversion Matrix + +The system supports conversions between all dashboard API versions: + +| From ↓ / To → | v0alpha1 | v1beta1 | v2alpha1 | v2beta1 | +|---------------|----------|---------|----------|---------| +| **v0alpha1** | ✓ | ✓ | ✓ | ✓ | +| **v1beta1** | ✓ | ✓ | ✓ | ✓ | +| **v2alpha1** | ✓ | ✓ | ✓ | ✓ | +| **v2beta1** | ✓ | ✓ | ✓ | ✓ | + +Each conversion path is automatically instrumented with metrics and logging. + +## API Versions + +The supported dashboard API versions are: + +- `dashboard.grafana.app/v0alpha1` - Legacy JSON dashboard format +- `dashboard.grafana.app/v1beta1` - Migrated JSON dashboard format +- `dashboard.grafana.app/v2alpha1` - New structured dashboard format +- `dashboard.grafana.app/v2beta1` - Enhanced structured dashboard format + +## Schema Versions + +Schema versions (v13-v42) apply only to v0alpha1 and v1beta1 dashboards: + +- **Minimum supported version**: v13 +- **Latest version**: v42 +- **Migration path**: Sequential (v13→v14→v15...→v42) + +For detailed information about creating schema version migrations, see the [SchemaVersion Migration Guide](./schemaversion/README.md). + +## Testing + +The implementation includes comprehensive test coverage for conversion-level operations: + +- **Backend conversion tests**: API version conversions with metrics validation +- **Frontend tests**: TypeScript conversion tests +- **Integration tests**: End-to-end conversion validation +- **Metrics tests**: Prometheus counter validation + +### Backend conversion tests + +The backend conversion tests validate API version conversions and metrics instrumentation: + +- **API conversion tests**: Test conversions between v0alpha1, v1beta1, v2alpha1, v2beta1 +- **Metrics validation**: Tests verify that conversion metrics are properly recorded +- **Error handling**: Tests validate error classification and logging +- **Performance**: Tests ensure conversion operations are efficient + +**Test execution:** +```bash +# All backend conversion tests +go test ./apps/dashboard/pkg/migration/conversion/... -v + +# Metrics validation tests +go test ./apps/dashboard/pkg/migration/... -run TestSchemaMigrationMetrics +``` + +### Frontend migration comparison tests + +The frontend migration comparison tests validate that backend and frontend conversion logic produce consistent results: + +- **Test methodology**: Compares backend vs frontend conversion outputs through DashboardModel integration +- **Dataset coverage**: Tests run against curated test files covering various conversion scenarios +- **Test location**: `public/app/features/dashboard/state/DashboardMigratorToBackend.test.ts` +- **Test data**: Located in `apps/dashboard/pkg/migration/testdata/input/` and `testdata/output/` + +**Test execution:** +```bash +# Frontend migration comparison tests +yarn test DashboardMigratorToBackend.test.ts +``` + +**Test approach:** +- **Frontend path**: `jsonInput → DashboardModel → DashboardMigrator → getSaveModelClone()` +- **Backend path**: `jsonInput → Backend Conversion → backendOutput → DashboardModel → getSaveModelClone()` +- **Comparison**: Direct comparison of final converted states from both paths + +For schema version migration testing details, see the [SchemaVersion Migration Guide](./schemaversion/README.md). + +## Monitoring Migrations + +The dashboard migration system provides comprehensive observability through metrics, logging, and error classification to monitor conversion operations. + +### Metrics The dashboard migration system now provides comprehensive observability through: - **Prometheus metrics** for tracking conversion success/failure rates and performance @@ -12,7 +146,7 @@ The dashboard migration system now provides comprehensive observability through: - **Automatic instrumentation** via wrapper functions that eliminate code duplication - **Error classification** to distinguish between different types of migration failures -### 1. Dashboard conversion success metric +#### 1. Dashboard conversion success metric **Metric Name:** `grafana_dashboard_migration_conversion_success_total` @@ -36,7 +170,7 @@ grafana_dashboard_migration_conversion_success_total{ } 1250 ``` -### 2. Dashboard conversion failure metric +#### 2. Dashboard conversion failure metric **Metric Name:** `grafana_dashboard_migration_conversion_failure_total` @@ -62,28 +196,28 @@ grafana_dashboard_migration_conversion_failure_total{ } 42 ``` -## Error types +### Error Types The `error_type` label classifies failures into three categories: -### 1. `conversion_error` +#### 1. `conversion_error` - General conversion failures not related to schema migration - API-level conversion issues - Programming errors in conversion functions -### 2. `schema_version_migration_error` +#### 2. `schema_version_migration_error` - Failures during individual schema version migrations (v14→v15, v15→v16, etc.) - Schema-specific transformation errors - Data format incompatibilities -### 3. `schema_minimum_version_error` +#### 3. `schema_minimum_version_error` - Dashboards with schema versions below the minimum supported version (< v13) - These are logged as warnings rather than errors - Indicates dashboards that cannot be migrated automatically -## Logging +### Logging -### Log structure +#### Log structure All migration logs use structured logging with consistent field names: @@ -102,9 +236,9 @@ All migration logs use structured logging with consistent field names: - `erroredConversionFunc` - Name of the conversion function that failed - `error` - The actual error message -### Log levels +#### Log levels -#### Success (DEBUG level) +##### Success (DEBUG level) ```json { "level": "debug", @@ -117,7 +251,7 @@ All migration logs use structured logging with consistent field names: } ``` -#### Conversion/Migration Error (ERROR level) +##### Conversion/Migration Error (ERROR level) ```json { "level": "error", @@ -134,7 +268,7 @@ All migration logs use structured logging with consistent field names: } ``` -#### Minimum Version Error (WARN level) +##### Minimum Version Error (WARN level) ```json { "level": "warn", @@ -151,9 +285,9 @@ All migration logs use structured logging with consistent field names: } ``` -## Implementation details +### Implementation Details -### Automatic instrumentation +#### Automatic instrumentation All dashboard conversions are automatically instrumented via the `withConversionMetrics` wrapper function: @@ -165,7 +299,7 @@ s.AddConversionFunc((*dashv0.Dashboard)(nil), (*dashv1.Dashboard)(nil), })) ``` -### Error handling +#### Error handling Custom error types provide structured error information: @@ -187,9 +321,9 @@ type ConversionError struct { } ``` -## Registration +### Registration -### Metrics registration +#### Metrics registration Metrics must be registered with Prometheus during service initialization: @@ -200,7 +334,7 @@ import "github.com/grafana/grafana/apps/dashboard/pkg/migration" migration.RegisterMetrics(prometheusRegistry) ``` -### Available metrics +#### Available metrics The following metrics are available after registration: @@ -212,86 +346,7 @@ migration.MDashboardConversionSuccessTotal migration.MDashboardConversionFailureTotal ``` -## Conversion matrix - -The system supports conversions between all dashboard API versions: - -| From ↓ / To → | v0alpha1 | v1beta1 | v2alpha1 | v2beta1 | -|---------------|----------|---------|----------|---------| -| **v0alpha1** | ✓ | ✓ | ✓ | ✓ | -| **v1beta1** | ✓ | ✓ | ✓ | ✓ | -| **v2alpha1** | ✓ | ✓ | ✓ | ✓ | -| **v2beta1** | ✓ | ✓ | ✓ | ✓ | - -Each conversion path is automatically instrumented with metrics and logging. - -## API versions - -The supported dashboard API versions are: - -- `dashboard.grafana.app/v0alpha1` - Legacy JSON dashboard format -- `dashboard.grafana.app/v1beta1` - Migrated JSON dashboard format -- `dashboard.grafana.app/v2alpha1` - New structured dashboard format -- `dashboard.grafana.app/v2beta1` - Enhanced structured dashboard format - -## Schema versions - -Schema versions (v13-v41) apply only to v0alpha1 and v1beta1 dashboards: - -- **Minimum supported version**: v13 -- **Latest version**: v41 -- **Migration path**: Sequential (v13→v14→v15...→v41) - -## Migration testing - -The implementation includes comprehensive test coverage: - -- **Backend tests**: Go migration tests with metrics validation -- **Frontend tests**: TypeScript conversion tests -- **Integration tests**: End-to-end conversion validation -- **Metrics tests**: Prometheus counter validation - -### Backend migration tests - -The backend migration tests validate schema version migrations and API conversions: - -- **Schema migration tests**: Test individual schema version upgrades (v14→v15, v15→v16, etc.) -- **Conversion tests**: Test API version conversions with automatic metrics instrumentation -- **Test data**: Uses curated test files from `testdata/input/` covering schema versions 14-41 -- **Metrics validation**: Tests verify that conversion metrics are properly recorded - -**Test execution:** -```bash -# All backend migration tests -go test ./apps/dashboard/pkg/migration/... -v - -# Schema migration tests only -go test ./apps/dashboard/pkg/migration/ -v - -# API conversion tests with metrics -go test ./apps/dashboard/pkg/migration/conversion/... -v -``` - -### Frontend migration comparison tests - -The frontend migration comparison tests validate that backend and frontend migration logic produce consistent results: - -- **Test methodology**: Compares backend vs frontend migration outputs through DashboardModel integration -- **Dataset coverage**: Tests run against 42 curated test files spanning schema versions 14-41 -- **Test location**: `public/app/features/dashboard/state/DashboardMigratorToBackend.test.ts` -- **Test data**: Located in `apps/dashboard/pkg/migration/testdata/input/` and `testdata/output/` - -**Test execution:** -```bash -# Frontend migration comparison tests -yarn test DashboardMigratorToBackend.test.ts -``` - -**Test approach:** -- **Frontend path**: `jsonInput → DashboardModel → DashboardMigrator → getSaveModelClone()` -- **Backend path**: `jsonInput → Backend Migration → backendOutput → DashboardModel → getSaveModelClone()` -- **Comparison**: Direct comparison of final migrated states from both paths - -## Related documentation +## Related Documentation +- [Schema Migration Guide](./schemaversion/README.md) - Complete guide for creating new dashboard schema migrations - [PR #110178 - Dashboard migration: Add missing metrics registration](https://github.com/grafana/grafana/pull/110178) diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v0alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v0alpha1.json index 1191a6aef3b..3306c334c5e 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v0alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v0alpha1.json @@ -15,7 +15,505 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-complete", + "labels": { + "category": "test" + }, + "annotations": { + "description": "Complete example of v2alpha1 dashboard features" + } + }, + "spec": { + "annotations": [ + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "query": { + "kind": "grafana", + "spec": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + } + }, + "enable": true, + "hide": false, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "builtIn": true + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "changes(process_start_time_seconds[1m])", + "refId": "Anno" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "Prometheus Annotations", + "builtIn": false + } + } + ], + "cursorSync": "Tooltip", + "description": "This dashboard demonstrates all features that need to be converted from v2alpha1 to v2beta1", + "editable": true, + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Panel with Conditional Rendering", + "description": "This panel demonstrates conditional rendering features", + "links": null, + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "expr": "up{job=\"grafana\"}" + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [ + { + "kind": "reduce", + "spec": { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "mean" + ] + } + } + } + ], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "stat", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "textMode": "auto" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "type": "value", + "options": { + "0": { + "text": "Down", + "color": "red" + }, + "1": { + "text": "Up", + "color": "green" + } + } + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "red" + }, + { + "value": 1, + "color": "green" + } + ] + }, + "color": { + "mode": "thresholds" + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "RowsLayout", + "spec": { + "rows": [ + { + "kind": "Row", + "spec": { + "title": "Conditional Row", + "collapse": false, + "hideHeader": false, + "fillScreen": false, + "conditionalRendering": { + "kind": "ConditionalRenderingGroup", + "spec": { + "visibility": "show", + "condition": "and", + "items": [ + { + "kind": "ConditionalRenderingVariable", + "spec": { + "variable": "group_by", + "operator": "includes", + "value": "instance" + } + }, + { + "kind": "ConditionalRenderingData", + "spec": { + "value": true + } + }, + { + "kind": "ConditionalRenderingTimeRangeSize", + "spec": { + "value": "1h" + } + } + ] + } + }, + "layout": { + "kind": "GridLayout", + "spec": { + "items": [ + { + "kind": "GridLayoutItem", + "spec": { + "x": 0, + "y": 0, + "width": 24, + "height": 8, + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + } + ] + } + } + } + } + ] + } + }, + "links": null, + "liveNow": true, + "preload": true, + "tags": [ + "test", + "example", + "migration" + ], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "10s", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "weekStart": "monday", + "fiscalYearStartMonth": 0 + }, + "title": "Test: Complete V2alpha1 Dashboard Example", + "variables": [ + { + "kind": "QueryVariable", + "spec": { + "name": "prometheus_query", + "current": { + "text": "All", + "value": [ + "$__all" + ] + }, + "label": "Prometheus Query", + "hide": "dontHide", + "refresh": "time", + "skipUrlSync": false, + "description": "Shows all up metrics", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "alphabetical", + "definition": "up", + "options": null, + "multi": true, + "includeAll": true, + "allowCustomValue": false + } + }, + { + "kind": "TextVariable", + "spec": { + "name": "text_var", + "current": { + "selected": true, + "text": "server1", + "value": "server1" + }, + "query": "server1,server2,server3", + "label": "Text Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "A simple text variable" + } + }, + { + "kind": "ConstantVariable", + "spec": { + "name": "constant_var", + "query": "production", + "current": { + "selected": true, + "text": "production", + "value": "production" + }, + "label": "Constant", + "hide": "dontHide", + "skipUrlSync": true, + "description": "A constant value" + } + }, + { + "kind": "DatasourceVariable", + "spec": { + "name": "ds_var", + "pluginId": "prometheus", + "refresh": "load", + "regex": "/^gdev-/", + "current": { + "text": "gdev-prometheus", + "value": "gdev-prometheus" + }, + "options": [ + { + "text": "gdev-prometheus", + "value": "gdev-prometheus" + } + ], + "multi": false, + "includeAll": false, + "label": "Datasource", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Select a datasource", + "allowCustomValue": false + } + }, + { + "kind": "IntervalVariable", + "spec": { + "name": "interval", + "query": "1m,5m,10m,30m,1h,6h,12h,1d", + "current": { + "selected": true, + "text": "5m", + "value": "5m" + }, + "options": [ + { + "text": "1m", + "value": "1m" + }, + { + "text": "5m", + "value": "5m" + }, + { + "text": "10m", + "value": "10m" + }, + { + "text": "30m", + "value": "30m" + }, + { + "text": "1h", + "value": "1h" + }, + { + "text": "6h", + "value": "6h" + }, + { + "text": "12h", + "value": "12h" + }, + { + "text": "1d", + "value": "1d" + } + ], + "auto": true, + "auto_min": "10s", + "auto_count": 30, + "refresh": "load", + "label": "Interval", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Time interval selection" + } + }, + { + "kind": "CustomVariable", + "spec": { + "name": "custom_var", + "query": "prod : Production, staging : Staging, dev : Development", + "current": { + "text": [ + "Production" + ], + "value": [ + "prod" + ] + }, + "options": [ + { + "text": "Production", + "value": "prod" + }, + { + "text": "Staging", + "value": "staging" + }, + { + "text": "Development", + "value": "dev" + } + ], + "multi": true, + "includeAll": true, + "allValue": "*", + "label": "Custom Options", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Custom multi-value variable", + "allowCustomValue": true + } + }, + { + "kind": "GroupByVariable", + "spec": { + "name": "group_by", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "current": { + "text": "instance", + "value": "instance" + }, + "options": null, + "multi": false, + "label": "Group By", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Group metrics by label" + } + }, + { + "kind": "AdhocVariable", + "spec": { + "name": "filters", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "baseFilters": [ + { + "key": "job", + "operator": "=", + "value": "grafana", + "condition": "AND" + } + ], + "filters": [], + "defaultKeys": [ + { + "text": "job", + "value": "job", + "expandable": true + }, + { + "text": "instance", + "value": "instance", + "expandable": true + } + ], + "label": "Filters", + "hide": "dontHide", + "skipUrlSync": false, + "allowCustomValue": false + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v1beta1.json index ed784b45834..c55f325789d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v1beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.complete.v1beta1.json @@ -15,7 +15,505 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-complete", + "labels": { + "category": "test" + }, + "annotations": { + "description": "Complete example of v2alpha1 dashboard features" + } + }, + "spec": { + "annotations": [ + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "query": { + "kind": "grafana", + "spec": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + } + }, + "enable": true, + "hide": false, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "builtIn": true + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "changes(process_start_time_seconds[1m])", + "refId": "Anno" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "Prometheus Annotations", + "builtIn": false + } + } + ], + "cursorSync": "Tooltip", + "description": "This dashboard demonstrates all features that need to be converted from v2alpha1 to v2beta1", + "editable": true, + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Panel with Conditional Rendering", + "description": "This panel demonstrates conditional rendering features", + "links": null, + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "expr": "up{job=\"grafana\"}" + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [ + { + "kind": "reduce", + "spec": { + "id": "reduce", + "options": { + "includeTimeField": false, + "mode": "reduceFields", + "reducers": [ + "mean" + ] + } + } + } + ], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "stat", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "textMode": "auto" + }, + "fieldConfig": { + "defaults": { + "mappings": [ + { + "type": "value", + "options": { + "0": { + "text": "Down", + "color": "red" + }, + "1": { + "text": "Up", + "color": "green" + } + } + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "red" + }, + { + "value": 1, + "color": "green" + } + ] + }, + "color": { + "mode": "thresholds" + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "RowsLayout", + "spec": { + "rows": [ + { + "kind": "Row", + "spec": { + "title": "Conditional Row", + "collapse": false, + "hideHeader": false, + "fillScreen": false, + "conditionalRendering": { + "kind": "ConditionalRenderingGroup", + "spec": { + "visibility": "show", + "condition": "and", + "items": [ + { + "kind": "ConditionalRenderingVariable", + "spec": { + "variable": "group_by", + "operator": "includes", + "value": "instance" + } + }, + { + "kind": "ConditionalRenderingData", + "spec": { + "value": true + } + }, + { + "kind": "ConditionalRenderingTimeRangeSize", + "spec": { + "value": "1h" + } + } + ] + } + }, + "layout": { + "kind": "GridLayout", + "spec": { + "items": [ + { + "kind": "GridLayoutItem", + "spec": { + "x": 0, + "y": 0, + "width": 24, + "height": 8, + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + } + ] + } + } + } + } + ] + } + }, + "links": null, + "liveNow": true, + "preload": true, + "tags": [ + "test", + "example", + "migration" + ], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "10s", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "weekStart": "monday", + "fiscalYearStartMonth": 0 + }, + "title": "Test: Complete V2alpha1 Dashboard Example", + "variables": [ + { + "kind": "QueryVariable", + "spec": { + "name": "prometheus_query", + "current": { + "text": "All", + "value": [ + "$__all" + ] + }, + "label": "Prometheus Query", + "hide": "dontHide", + "refresh": "time", + "skipUrlSync": false, + "description": "Shows all up metrics", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "alphabetical", + "definition": "up", + "options": null, + "multi": true, + "includeAll": true, + "allowCustomValue": false + } + }, + { + "kind": "TextVariable", + "spec": { + "name": "text_var", + "current": { + "selected": true, + "text": "server1", + "value": "server1" + }, + "query": "server1,server2,server3", + "label": "Text Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "A simple text variable" + } + }, + { + "kind": "ConstantVariable", + "spec": { + "name": "constant_var", + "query": "production", + "current": { + "selected": true, + "text": "production", + "value": "production" + }, + "label": "Constant", + "hide": "dontHide", + "skipUrlSync": true, + "description": "A constant value" + } + }, + { + "kind": "DatasourceVariable", + "spec": { + "name": "ds_var", + "pluginId": "prometheus", + "refresh": "load", + "regex": "/^gdev-/", + "current": { + "text": "gdev-prometheus", + "value": "gdev-prometheus" + }, + "options": [ + { + "text": "gdev-prometheus", + "value": "gdev-prometheus" + } + ], + "multi": false, + "includeAll": false, + "label": "Datasource", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Select a datasource", + "allowCustomValue": false + } + }, + { + "kind": "IntervalVariable", + "spec": { + "name": "interval", + "query": "1m,5m,10m,30m,1h,6h,12h,1d", + "current": { + "selected": true, + "text": "5m", + "value": "5m" + }, + "options": [ + { + "text": "1m", + "value": "1m" + }, + { + "text": "5m", + "value": "5m" + }, + { + "text": "10m", + "value": "10m" + }, + { + "text": "30m", + "value": "30m" + }, + { + "text": "1h", + "value": "1h" + }, + { + "text": "6h", + "value": "6h" + }, + { + "text": "12h", + "value": "12h" + }, + { + "text": "1d", + "value": "1d" + } + ], + "auto": true, + "auto_min": "10s", + "auto_count": 30, + "refresh": "load", + "label": "Interval", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Time interval selection" + } + }, + { + "kind": "CustomVariable", + "spec": { + "name": "custom_var", + "query": "prod : Production, staging : Staging, dev : Development", + "current": { + "text": [ + "Production" + ], + "value": [ + "prod" + ] + }, + "options": [ + { + "text": "Production", + "value": "prod" + }, + { + "text": "Staging", + "value": "staging" + }, + { + "text": "Development", + "value": "dev" + } + ], + "multi": true, + "includeAll": true, + "allValue": "*", + "label": "Custom Options", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Custom multi-value variable", + "allowCustomValue": true + } + }, + { + "kind": "GroupByVariable", + "spec": { + "name": "group_by", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "current": { + "text": "instance", + "value": "instance" + }, + "options": null, + "multi": false, + "label": "Group By", + "hide": "dontHide", + "skipUrlSync": false, + "description": "Group metrics by label" + } + }, + { + "kind": "AdhocVariable", + "spec": { + "name": "filters", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "baseFilters": [ + { + "key": "job", + "operator": "=", + "value": "grafana", + "condition": "AND" + } + ], + "filters": [], + "defaultKeys": [ + { + "text": "job", + "value": "job", + "expandable": true + }, + { + "text": "instance", + "value": "instance", + "expandable": true + } + ], + "label": "Filters", + "hide": "dontHide", + "skipUrlSync": false, + "allowCustomValue": false + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v0alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v0alpha1.json index 46365be1c23..4497423cc53 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v0alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v0alpha1.json @@ -9,7 +9,1055 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-annotations" + }, + "spec": { + "annotations": [ + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "query": { + "kind": "grafana", + "spec": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + } + }, + "enable": true, + "hide": false, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "builtIn": true + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "blue", + "name": "testdata-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "blue", + "name": "no-ds-testdata-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "{action=\"add_client\"}", + "interval": "", + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "prom-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "expr": "{action=\"add_client\"}", + "interval": "", + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "no-ds-prom-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "PBBCEC2D313BC06C3" + }, + "query": { + "kind": "grafana-postgresql-datasource", + "spec": { + "editorMode": "builder", + "format": "table", + "lines": 10, + "rawSql": "", + "refId": "Anno", + "scenarioId": "annotations", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "postgress-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "query": { + "kind": "elasticsearch", + "spec": { + "lines": 10, + "query": "test query", + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "elastic - annos", + "builtIn": false, + "legacyOptions": { + "tagsField": "asd", + "textField": "asd", + "timeEndField": "asdas", + "timeField": "asd" + } + } + } + ], + "cursorSync": "Off", + "editable": true, + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Simple timeseries (WITH DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 3 + } + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-2": { + "kind": "Panel", + "spec": { + "id": 2, + "title": "Simple stat (NO DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 4 + } + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "stat", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "thresholds" + } + }, + "overrides": [] + } + } + } + } + }, + "panel-3": { + "kind": "Panel", + "spec": { + "id": 3, + "title": "Panel with NO REF to gdev-prometheus", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-4": { + "kind": "Panel", + "spec": { + "id": 4, + "title": "Panel with ref to gdev-prometheus", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-5": { + "kind": "Panel", + "spec": { + "id": 5, + "title": "Mixed DS WITH REFS", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests{server=\"backend-01\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + }, + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "B", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-6": { + "kind": "Panel", + "spec": { + "id": 6, + "title": "Mixed DS WITHOUT REFS", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests{server=\"backend-01\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "refId": "A", + "hidden": false + } + }, + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "refId": "B", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "AutoGridLayout", + "spec": { + "maxColumnCount": 3, + "columnWidthMode": "standard", + "rowHeightMode": "standard", + "items": [ + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-3" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-4" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-2" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-5" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-6" + } + } + } + ] + } + }, + "links": [], + "liveNow": false, + "preload": false, + "tags": [], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with annotations", + "variables": [ + { + "kind": "QueryVariable", + "spec": { + "name": "variable-ds-prometheus", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "datasource": { + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-ds", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "csv": "1,2,3,4", + "scenarioId": "csv_metric_values" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-ds-empty-query", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-default-ds", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v1beta1.json index a0cf65052a7..11151fc1345 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v1beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.ds-data-query.v1beta1.json @@ -9,7 +9,1055 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-annotations" + }, + "spec": { + "annotations": [ + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "query": { + "kind": "grafana", + "spec": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + } + }, + "enable": true, + "hide": false, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "builtIn": true + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "blue", + "name": "testdata-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "blue", + "name": "no-ds-testdata-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "{action=\"add_client\"}", + "interval": "", + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "prom-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "expr": "{action=\"add_client\"}", + "interval": "", + "lines": 10, + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "yellow", + "name": "no-ds-prom-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "PBBCEC2D313BC06C3" + }, + "query": { + "kind": "grafana-postgresql-datasource", + "spec": { + "editorMode": "builder", + "format": "table", + "lines": 10, + "rawSql": "", + "refId": "Anno", + "scenarioId": "annotations", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "postgress-annos", + "builtIn": false + } + }, + { + "kind": "AnnotationQuery", + "spec": { + "datasource": { + "type": "elasticsearch", + "uid": "gdev-elasticsearch" + }, + "query": { + "kind": "elasticsearch", + "spec": { + "lines": 10, + "query": "test query", + "refId": "Anno", + "scenarioId": "annotations" + } + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "elastic - annos", + "builtIn": false, + "legacyOptions": { + "tagsField": "asd", + "textField": "asd", + "timeEndField": "asdas", + "timeField": "asd" + } + } + } + ], + "cursorSync": "Off", + "editable": true, + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Simple timeseries (WITH DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 3 + } + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-2": { + "kind": "Panel", + "spec": { + "id": 2, + "title": "Simple stat (NO DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 4 + } + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "stat", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "thresholds" + } + }, + "overrides": [] + } + } + } + } + }, + "panel-3": { + "kind": "Panel", + "spec": { + "id": 3, + "title": "Panel with NO REF to gdev-prometheus", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-4": { + "kind": "Panel", + "spec": { + "id": 4, + "title": "Panel with ref to gdev-prometheus", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-5": { + "kind": "Panel", + "spec": { + "id": 5, + "title": "Mixed DS WITH REFS", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests{server=\"backend-01\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "datasource": { + "uid": "gdev-prometheus" + }, + "refId": "A", + "hidden": false + } + }, + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "B", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + }, + "panel-6": { + "kind": "Panel", + "spec": { + "id": 6, + "title": "Mixed DS WITHOUT REFS", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "prometheus", + "spec": { + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(counters_requests{server=\"backend-01\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "__auto", + "range": true, + "useBackend": false + } + }, + "refId": "A", + "hidden": false + } + }, + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "refId": "B", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "AutoGridLayout", + "spec": { + "maxColumnCount": 3, + "columnWidthMode": "standard", + "rowHeightMode": "standard", + "items": [ + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-3" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-4" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-2" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-5" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-6" + } + } + } + ] + } + }, + "links": [], + "liveNow": false, + "preload": false, + "tags": [], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with annotations", + "variables": [ + { + "kind": "QueryVariable", + "spec": { + "name": "variable-ds-prometheus", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "datasource": { + "uid": "gdev-prometheus" + }, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-ds", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "csv": "1,2,3,4", + "scenarioId": "csv_metric_values" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-ds-empty-query", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "grafana-testdata-datasource", + "spec": {} + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + }, + { + "kind": "QueryVariable", + "spec": { + "name": "variable-no-default-ds", + "current": { + "text": null, + "value": null + }, + "hide": "dontHide", + "refresh": "", + "skipUrlSync": false, + "query": { + "kind": "prometheus", + "spec": { + "expr": "up" + } + }, + "regex": "", + "sort": "", + "options": null, + "multi": false, + "includeAll": false, + "allowCustomValue": false + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v0alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v0alpha1.json index add335f4b5e..ae50ed46c5d 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v0alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v0alpha1.json @@ -9,7 +9,100 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-groupby-adhoc-vars" + }, + "spec": { + "annotations": null, + "cursorSync": "", + "elements": null, + "layout": null, + "links": null, + "preload": false, + "tags": null, + "timeSettings": { + "from": "", + "to": "", + "autoRefresh": "", + "autoRefreshIntervals": null, + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with group by and adhoc variables", + "variables": [ + { + "kind": "GroupByVariable", + "spec": { + "name": "", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "current": { + "text": "text7", + "value": "value7" + }, + "options": null, + "multi": false, + "label": "Group By Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "A group by variable" + } + }, + { + "kind": "AdhocVariable", + "spec": { + "name": "adhocVar", + "datasource": { + "type": "prometheus", + "uid": "datasource-3" + }, + "baseFilters": [ + { + "key": "key1", + "operator": "=", + "value": "value1", + "condition": "AND" + }, + { + "key": "key2", + "operator": "=", + "value": "value2", + "condition": "OR" + } + ], + "filters": [ + { + "key": "key3", + "operator": "=", + "value": "value3", + "condition": "AND" + } + ], + "defaultKeys": [ + { + "text": "defaultKey1", + "value": "defaultKey1", + "group": "defaultGroup1", + "expandable": true + } + ], + "label": "Adhoc Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "An adhoc variable", + "allowCustomValue": true + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v1beta1.json index 4656a14bc7b..4f7ce909299 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v1beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.groupby-adhoc-vars.v1beta1.json @@ -9,7 +9,100 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-groupby-adhoc-vars" + }, + "spec": { + "annotations": null, + "cursorSync": "", + "elements": null, + "layout": null, + "links": null, + "preload": false, + "tags": null, + "timeSettings": { + "from": "", + "to": "", + "autoRefresh": "", + "autoRefreshIntervals": null, + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with group by and adhoc variables", + "variables": [ + { + "kind": "GroupByVariable", + "spec": { + "name": "", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "current": { + "text": "text7", + "value": "value7" + }, + "options": null, + "multi": false, + "label": "Group By Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "A group by variable" + } + }, + { + "kind": "AdhocVariable", + "spec": { + "name": "adhocVar", + "datasource": { + "type": "prometheus", + "uid": "datasource-3" + }, + "baseFilters": [ + { + "key": "key1", + "operator": "=", + "value": "value1", + "condition": "AND" + }, + { + "key": "key2", + "operator": "=", + "value": "value2", + "condition": "OR" + } + ], + "filters": [ + { + "key": "key3", + "operator": "=", + "value": "value3", + "condition": "AND" + } + ], + "defaultKeys": [ + { + "text": "defaultKey1", + "value": "defaultKey1", + "group": "defaultGroup1", + "expandable": true + } + ], + "label": "Adhoc Variable", + "hide": "dontHide", + "skipUrlSync": false, + "description": "An adhoc variable", + "allowCustomValue": true + } + } + ] + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v0alpha1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v0alpha1.json index 86e7f731622..acde79a0bcc 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v0alpha1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v0alpha1.json @@ -9,7 +9,219 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-viz-config" + }, + "spec": { + "annotations": null, + "cursorSync": "", + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Simple timeseries (WITH DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 3 + } + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "AutoGridLayout", + "spec": { + "maxColumnCount": 3, + "columnWidthMode": "standard", + "rowHeightMode": "standard", + "items": [ + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-3" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-4" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-2" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-5" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-6" + } + } + } + ] + } + }, + "links": [], + "liveNow": false, + "preload": false, + "tags": [], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with viz config", + "variables": null + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v1beta1.json b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v1beta1.json index cc538fe2f92..e0812fa1a2b 100644 --- a/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v1beta1.json +++ b/apps/dashboard/pkg/migration/conversion/testdata/output/v2alpha1.viz-config.v1beta1.json @@ -9,7 +9,219 @@ "conversion": { "failed": true, "error": "backend conversion not yet implemented", - "storedVersion": "v2alpha1" + "storedVersion": "v2alpha1", + "source": { + "kind": "Dashboard", + "apiVersion": "dashboard.grafana.app/v2alpha1", + "metadata": { + "name": "test-v2alpha1-viz-config" + }, + "spec": { + "annotations": null, + "cursorSync": "", + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "id": 1, + "title": "Simple timeseries (WITH DS REF)", + "description": "", + "links": [], + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "query": { + "kind": "grafana-testdata-datasource", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 3 + } + }, + "datasource": { + "uid": "gdev-testdata" + }, + "refId": "A", + "hidden": false + } + } + ], + "transformations": [], + "queryOptions": {} + } + }, + "vizConfig": { + "kind": "timeseries", + "spec": { + "pluginVersion": "12.1.0-pre", + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "fieldConfig": { + "defaults": { + "thresholds": { + "mode": "absolute", + "steps": [ + { + "value": 0, + "color": "green" + }, + { + "value": 80, + "color": "red" + } + ] + }, + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + } + }, + "overrides": [] + } + } + } + } + } + }, + "layout": { + "kind": "AutoGridLayout", + "spec": { + "maxColumnCount": 3, + "columnWidthMode": "standard", + "rowHeightMode": "standard", + "items": [ + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-3" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-4" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-2" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-1" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-5" + } + } + }, + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-6" + } + } + } + ] + } + }, + "links": [], + "liveNow": false, + "preload": false, + "tags": [], + "timeSettings": { + "timezone": "browser", + "from": "now-6h", + "to": "now", + "autoRefresh": "", + "autoRefreshIntervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "hideTimepicker": false, + "fiscalYearStartMonth": 0 + }, + "title": "Test: V2alpha1 dashboard with viz config", + "variables": null + }, + "status": {} + } } } } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/conversion/v0.go b/apps/dashboard/pkg/migration/conversion/v0.go index a1ccc0e5a6c..9eb42167072 100644 --- a/apps/dashboard/pkg/migration/conversion/v0.go +++ b/apps/dashboard/pkg/migration/conversion/v0.go @@ -78,6 +78,7 @@ func Convert_V0_to_V2alpha1(in *dashv0.Dashboard, out *dashv2alpha1.Dashboard, s StoredVersion: ptr.To(dashv0.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -94,6 +95,7 @@ func Convert_V0_to_V2beta1(in *dashv0.Dashboard, out *dashv2beta1.Dashboard, sco StoredVersion: ptr.To(dashv0.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } diff --git a/apps/dashboard/pkg/migration/conversion/v1.go b/apps/dashboard/pkg/migration/conversion/v1.go index 2dd51ffff03..86b3827af27 100644 --- a/apps/dashboard/pkg/migration/conversion/v1.go +++ b/apps/dashboard/pkg/migration/conversion/v1.go @@ -49,6 +49,7 @@ func Convert_V1_to_V2alpha1(in *dashv1.Dashboard, out *dashv2alpha1.Dashboard, s StoredVersion: ptr.To(dashv1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -65,6 +66,7 @@ func Convert_V1_to_V2beta1(in *dashv1.Dashboard, out *dashv2beta1.Dashboard, sco StoredVersion: ptr.To(dashv1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } diff --git a/apps/dashboard/pkg/migration/conversion/v2.go b/apps/dashboard/pkg/migration/conversion/v2.go index a7dfb9f39fd..91fcd8cf07a 100644 --- a/apps/dashboard/pkg/migration/conversion/v2.go +++ b/apps/dashboard/pkg/migration/conversion/v2.go @@ -20,6 +20,7 @@ func Convert_V2alpha1_to_V0(in *dashv2alpha1.Dashboard, out *dashv0.Dashboard, s StoredVersion: ptr.To(dashv2alpha1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -36,6 +37,7 @@ func Convert_V2alpha1_to_V1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboard, s StoredVersion: ptr.To(dashv2alpha1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -52,6 +54,7 @@ func Convert_V2alpha1_to_V2beta1(in *dashv2alpha1.Dashboard, out *dashv2beta1.Da StoredVersion: ptr.To(dashv2alpha1.VERSION), Failed: true, Error: ptr.To(err.Error()), + Source: in, }, } @@ -79,6 +82,7 @@ func Convert_V2beta1_to_V0(in *dashv2beta1.Dashboard, out *dashv0.Dashboard, sco StoredVersion: ptr.To(dashv2beta1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -95,6 +99,7 @@ func Convert_V2beta1_to_V1(in *dashv2beta1.Dashboard, out *dashv1.Dashboard, sco StoredVersion: ptr.To(dashv2beta1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } @@ -111,6 +116,7 @@ func Convert_V2beta1_to_V2alpha1(in *dashv2beta1.Dashboard, out *dashv2alpha1.Da StoredVersion: ptr.To(dashv2beta1.VERSION), Failed: true, Error: ptr.To("backend conversion not yet implemented"), + Source: in, }, } diff --git a/apps/dashboard/pkg/migration/frontend_defaults.go b/apps/dashboard/pkg/migration/frontend_defaults.go index 6f4cbbf87a4..517c1eccc25 100644 --- a/apps/dashboard/pkg/migration/frontend_defaults.go +++ b/apps/dashboard/pkg/migration/frontend_defaults.go @@ -2,6 +2,8 @@ package migration import ( "sort" + + "github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" ) // applyFrontendDefaults applies all DashboardModel constructor defaults @@ -549,6 +551,7 @@ func cleanupPanelForSaveWithContext(panel map[string]interface{}, isNested bool) // Clean up internal markers delete(panel, "_originallyHadTransformations") + delete(panel, "_originallyHadFieldConfigCustom") } // filterDefaultValues removes properties that match the default values (matches frontend's isEqual logic) @@ -600,8 +603,21 @@ func filterDefaultValues(panel map[string]interface{}, originalProperties map[st for prop, defaultValue := range defaults { if panelValue, exists := panel[prop]; exists { if isEqual(panelValue, defaultValue) { - // Special case: fieldConfig is always removed if it matches defaults (frontend getSaveModel behavior) + // Special case: fieldConfig - handle preservation of original custom object first if prop == "fieldConfig" { + // Check if we need to preserve the custom object before removing fieldConfig + if panel["_originallyHadFieldConfigCustom"] == true { + // Ensure fieldConfig structure exists with custom object + if fieldConfig, ok := panelValue.(map[string]interface{}); ok { + if defaults, ok := fieldConfig["defaults"].(map[string]interface{}); ok { + if _, hasCustom := defaults["custom"]; !hasCustom { + defaults["custom"] = map[string]interface{}{} + } + // Don't remove fieldConfig if we added custom back + continue + } + } + } delete(panel, prop) } else { // Only remove if it wasn't originally present in the input @@ -616,12 +632,32 @@ func filterDefaultValues(panel map[string]interface{}, originalProperties map[st // Remove empty targets arrays (frontend removes them in cleanup) removeIfDefaultValue(panel, "targets", []interface{}{}) + // Handle case where fieldConfig was removed but originally had custom object + if panel["_originallyHadFieldConfigCustom"] == true { + if _, hasFieldConfig := panel["fieldConfig"]; !hasFieldConfig { + // Recreate fieldConfig with custom object + panel["fieldConfig"] = map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{}, + }, + "overrides": []interface{}{}, + } + } + } + // Clean up fieldConfig to match frontend behavior if fieldConfig, exists := panel["fieldConfig"].(map[string]interface{}); exists { // Clean up fieldConfig defaults to match frontend behavior if defaults, hasDefaults := fieldConfig["defaults"].(map[string]interface{}); hasDefaults { // Remove properties that frontend considers as defaults and omits cleanupFieldConfigDefaults(defaults, panel) + + // Preserve custom object if it was originally present, even if empty + if panel["_originallyHadFieldConfigCustom"] == true { + if _, hasCustom := defaults["custom"]; !hasCustom { + defaults["custom"] = map[string]interface{}{} + } + } } } } @@ -759,10 +795,12 @@ func cleanupVariable(variable map[string]interface{}) { if variableType, ok := variable["type"].(string); ok { switch variableType { case "query": - // Query variables: keep options: [] if refresh !== never - // Since refresh is not specified in the input, it defaults to not "never" - if _, hasOptions := variable["options"]; !hasOptions { - variable["options"] = []interface{}{} + // Query variables: keep options: [] if refresh !== never (matches frontend getSaveModel logic) + refresh := schemaversion.GetIntValue(variable, "refresh", 1) // Default to 1 (onDashboardLoad) if not specified + if refresh != 0 { // 0 = VariableRefreshNever + if _, hasOptions := variable["options"]; !hasOptions { + variable["options"] = []interface{}{} + } } case "constant": // Constant variables: remove options completely @@ -1015,6 +1053,7 @@ func cleanupDashboardDefaults(dashboard map[string]interface{}) { // These properties are lost during frontend's property copying loop in getSaveModelCloneOld() delete(dashboard, "preload") // Transient dashboard loading state delete(dashboard, "iteration") // Template variable iteration timestamp + delete(dashboard, "nav") } // cleanupFieldConfigDefaults removes properties that frontend considers as defaults and omits @@ -1085,3 +1124,36 @@ func trackPanelOriginalTransformations(panel map[string]interface{}) { } } } + +// trackOriginalFieldConfigCustom marks panels that had fieldConfig.defaults.custom in the original input +// This is needed to match frontend hasOwnProperty behavior and preserve empty custom objects +func trackOriginalFieldConfigCustom(dashboard map[string]interface{}) { + if panels, ok := dashboard["panels"].([]interface{}); ok { + for _, panelInterface := range panels { + if panel, ok := panelInterface.(map[string]interface{}); ok { + trackPanelOriginalFieldConfigCustom(panel) + } + } + } +} + +// trackPanelOriginalFieldConfigCustom recursively tracks fieldConfig.defaults.custom in panels and nested panels +func trackPanelOriginalFieldConfigCustom(panel map[string]interface{}) { + // Mark if this panel had fieldConfig.defaults.custom in original input + if fieldConfig, ok := panel["fieldConfig"].(map[string]interface{}); ok { + if defaults, ok := fieldConfig["defaults"].(map[string]interface{}); ok { + if _, hasCustom := defaults["custom"]; hasCustom { + panel["_originallyHadFieldConfigCustom"] = true + } + } + } + + // Handle nested panels in row panels + if nestedPanels, ok := panel["panels"].([]interface{}); ok { + for _, nestedPanelInterface := range nestedPanels { + if nestedPanel, ok := nestedPanelInterface.(map[string]interface{}); ok { + trackPanelOriginalFieldConfigCustom(nestedPanel) + } + } + } +} diff --git a/apps/dashboard/pkg/migration/frontend_defaults_test.go b/apps/dashboard/pkg/migration/frontend_defaults_test.go index b17a941164a..5bc126eacec 100644 --- a/apps/dashboard/pkg/migration/frontend_defaults_test.go +++ b/apps/dashboard/pkg/migration/frontend_defaults_test.go @@ -807,6 +807,193 @@ func TestTransformationsArrayContextAwareLogic(t *testing.T) { } } +func TestTrackOriginalFieldConfigCustom(t *testing.T) { + tests := []struct { + name string + input map[string]interface{} + expected map[string]interface{} + }{ + { + name: "track_top_level_panel_with_fieldConfig_custom", + input: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "timeseries", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{}, + }, + "overrides": []interface{}{}, + }, + }, + map[string]interface{}{ + "id": 2, + "type": "table", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{}, + "overrides": []interface{}{}, + }, + }, + map[string]interface{}{ + "id": 3, + "type": "stat", + "title": "Panel without fieldConfig", + }, + }, + }, + expected: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "timeseries", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{}, + }, + "overrides": []interface{}{}, + }, + "_originallyHadFieldConfigCustom": true, // marker added + }, + map[string]interface{}{ + "id": 2, + "type": "table", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{}, + "overrides": []interface{}{}, + }, + // no marker added - no custom object + }, + map[string]interface{}{ + "id": 3, + "type": "stat", + "title": "Panel without fieldConfig", + // no marker added - no fieldConfig + }, + }, + }, + }, + { + name: "track_nested_panels_in_row_with_fieldConfig_custom", + input: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "row", + "title": "Row Panel", + "panels": []interface{}{ + map[string]interface{}{ + "id": 10, + "type": "singlestat", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{}, + }, + "overrides": []interface{}{}, + }, + }, + map[string]interface{}{ + "id": 11, + "type": "graph", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "unit": "bytes", + }, + "overrides": []interface{}{}, + }, + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "row", + "title": "Row Panel", + "panels": []interface{}{ + map[string]interface{}{ + "id": 10, + "type": "singlestat", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{}, + }, + "overrides": []interface{}{}, + }, + "_originallyHadFieldConfigCustom": true, // marker added to nested panel + }, + map[string]interface{}{ + "id": 11, + "type": "graph", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "unit": "bytes", + }, + "overrides": []interface{}{}, + }, + // no marker added - no custom object + }, + }, + }, + }, + }, + }, + { + name: "track_panels_with_non_empty_custom", + input: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "timeseries", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{ + "axisPlacement": "left", + }, + }, + "overrides": []interface{}{}, + }, + }, + }, + }, + expected: map[string]interface{}{ + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "timeseries", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{ + "axisPlacement": "left", + }, + }, + "overrides": []interface{}{}, + }, + "_originallyHadFieldConfigCustom": true, // marker added for non-empty custom + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create a deep copy of input for testing + dashboard := deepCopy(tt.input).(map[string]interface{}) + + // Apply the tracking logic + trackOriginalFieldConfigCustom(dashboard) + + // Verify the result + if !compareValues(dashboard, tt.expected) { + t.Errorf("Test %s failed.\nExpected: %+v\nGot: %+v", tt.name, tt.expected, dashboard) + } + }) + } +} + func TestTrackOriginalTransformations(t *testing.T) { tests := []struct { name string @@ -915,6 +1102,123 @@ func TestTrackOriginalTransformations(t *testing.T) { } } +func TestCleanupPanelForSavePreservesOriginalFieldConfigCustom(t *testing.T) { + tests := []struct { + name string + input map[string]interface{} + expected map[string]interface{} + }{ + { + name: "preserve_empty_custom_when_originally_present", + input: map[string]interface{}{ + "type": "singlestat", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{}, // Empty but originally present + }, + "overrides": []interface{}{}, + }, + "_originallyHadFieldConfigCustom": true, // Marker indicating it was originally present + }, + expected: map[string]interface{}{ + "type": "stat", // Auto-migrated from singlestat + "autoMigrateFrom": "singlestat", // Auto-migration marker + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{}, // Should be preserved + }, + "overrides": []interface{}{}, + }, + }, + }, + { + name: "remove_empty_custom_when_not_originally_present", + input: map[string]interface{}{ + "type": "timeseries", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{}, // Empty and not originally present + }, + "overrides": []interface{}{}, + }, + // No marker - custom was not originally present + }, + expected: map[string]interface{}{ + "type": "timeseries", + // fieldConfig should be removed entirely as it matches defaults + }, + }, + { + name: "preserve_non_empty_custom_regardless_of_marker", + input: map[string]interface{}{ + "type": "table", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{ + "displayMode": "list", + }, + }, + "overrides": []interface{}{}, + }, + }, + expected: map[string]interface{}{ + "type": "table", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{ + "displayMode": "list", + }, + }, + "overrides": []interface{}{}, + }, + }, + }, + { + name: "add_custom_when_originally_present_but_missing", + input: map[string]interface{}{ + "type": "stat", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{}, // custom missing + "overrides": []interface{}{}, + }, + "_originallyHadFieldConfigCustom": true, // But marker indicates it was originally present + }, + expected: map[string]interface{}{ + "type": "stat", + "fieldConfig": map[string]interface{}{ + "defaults": map[string]interface{}{ + "custom": map[string]interface{}{}, // Should be added back + }, + "overrides": []interface{}{}, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + panel := make(map[string]interface{}) + for k, v := range tt.input { + panel[k] = v + } + + cleanupPanelForSaveWithContext(panel, false) + + // Verify expected properties exist + for key, expectedValue := range tt.expected { + if actualValue, exists := panel[key]; !exists { + t.Errorf("Property %s should exist but is missing", key) + } else if !compareValues(actualValue, expectedValue) { + t.Errorf("Property %s has wrong value. Expected: %v, Got: %v", key, expectedValue, actualValue) + } + } + + // Verify internal markers are cleaned up + assertPropertyRemoved(t, panel, "_originallyHadFieldConfigCustom") + }) + } +} + func TestCleanupPanelForSave(t *testing.T) { tests := []struct { name string diff --git a/apps/dashboard/pkg/migration/migrate.go b/apps/dashboard/pkg/migration/migrate.go index 62a24db3f2c..73fdf460416 100644 --- a/apps/dashboard/pkg/migration/migrate.go +++ b/apps/dashboard/pkg/migration/migrate.go @@ -66,6 +66,10 @@ func (m *migrator) migrate(ctx context.Context, dash map[string]interface{}, tar // This is needed to match frontend hasOwnProperty behavior trackOriginalTransformations(dash) + // 1.1. Track which panels had fieldConfig.defaults.custom in original input + // This is needed to preserve empty custom objects that were originally present + trackOriginalFieldConfigCustom(dash) + // 2. Apply ALL frontend defaults FIRST (DashboardModel + PanelModel defaults) // This replicates the behavior of the frontend DashboardModel and PanelModel constructors applyFrontendDefaults(dash) diff --git a/apps/dashboard/pkg/migration/schemaversion/README.md b/apps/dashboard/pkg/migration/schemaversion/README.md new file mode 100644 index 00000000000..e1cfdc45a95 --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/README.md @@ -0,0 +1,524 @@ +# Dashboard Schema Migration Guide + +This guide provides comprehensive instructions for creating new dashboard schema migrations in Grafana. + +## Table of Contents + +- [Overview](#overview) +- [Current Schema Versions](#current-schema-versions) +- [Creating a New Migration](#creating-a-new-migration) + - [Step 1: Create the Migration File](#step-1-create-the-migration-file) + - [Step 2: Register the Migration](#step-2-register-the-migration) + - [Step 3: Create Test Data](#step-3-create-test-data) + - [Step 4: Run Tests](#step-4-run-tests) + - [Step 5: Frontend-Backend Consistency Validation](#step-5-frontend-backend-consistency-validation) + - [Step 6: Unit Testing](#step-6-unit-testing) +- [Utility Functions](#utility-functions) + - [Type Conversion](#type-conversion) + - [Data Access](#data-access) +- [Common Pitfalls](#common-pitfalls) + - [Type Assertions](#1-type-assertions) + - [Null Handling](#2-null-handling) + - [Nested Panel Processing](#3-nested-panel-processing) +- [Migration Beyond v42](#migration-beyond-v42) + - [When to Add Schema Migrations vs Panel Migrations](#when-to-add-schema-migrations-vs-panel-migrations) +- [Testing Guidelines](#testing-guidelines) + - [Test File Naming](#test-file-naming) + - [Test Data Requirements](#test-data-requirements) + - [Running Tests](#running-tests) +- [Comprehensive Testing Strategy](#comprehensive-testing-strategy) + - [Backend Migration Tests](#backend-migration-tests) + - [Frontend Migration Comparison Tests](#frontend-migration-comparison-tests) + - [Single Version Migration Testing](#single-version-migration-testing) +- [Resources](#resources) +- [Getting Help](#getting-help) + +## Overview + +Dashboard schema migrations ensure backward compatibility as the dashboard data model evolves. Each migration transforms dashboards from one schema version to the next, allowing older dashboards to work with newer Grafana versions. + +## Current Schema Versions + +- **Minimum Version**: 13 +- **Latest Version**: 42 +- **Backend Location**: `apps/dashboard/pkg/migration/schemaversion/` +- **Frontend Location**: `public/app/features/dashboard/state/DashboardMigrator.ts` + +## Creating a New Migration + +### Step 1: Create the Migration File + +Create a new file: `v{N}.go` where `{N}` is the target schema version. + +```go +package schemaversion + +import "context" + +// V{N} migrates [describe what this migration does]. +// +// [Detailed description of the migration including:] +// - What properties are being changed +// - Why the migration is necessary +// - Any special handling or edge cases +// +// Example before migration: +// +// "panels": [ +// { +// "id": 1, +// "type": "graph", +// "oldProperty": "value" +// } +// ] +// +// Example after migration: +// +// "panels": [ +// { +// "id": 1, +// "type": "graph", +// "newProperty": "value" +// } +// ] +func V{N}(_ context.Context, dashboard map[string]interface{}) error { + dashboard["schemaVersion"] = {N} + + // Your migration logic here + // Transform dashboard properties as needed + + return nil +} +``` + +### Step 2: Register the Migration + +Add your migration to `migrations.go`: + +```go +func GetMigrations(dsInfoProvider DataSourceInfoProvider) map[int]SchemaVersionMigrationFunc { + return map[int]SchemaVersionMigrationFunc{ + // ... existing migrations + {N}: V{N}, // Add your migration here + // ... rest of migrations + } +} +``` + +### Step 3: Create Test Data + +#### Input Test File +Create `testdata/input/v{N}.{name}.json` with schema version `{N-1}` with scenarios to cover the specific migration: + +```json +{ + "title": "V{N} Migration Test Dashboard", + "schemaVersion": {N-1}, + "panels": [ + { + "id": 1, + "type": "graph", + "title": "Test Panel", + "oldProperty": "value" + } + ] +} +``` + +#### Expected Output +The test system will generate `testdata/output/latest_version/v{N}.{name}.v42.json` automatically. + +### Step 4: Run Tests + +```bash +# Run migration tests +go test ./apps/dashboard/pkg/migration/... + +# Run specific migration test +go test ./apps/dashboard/pkg/migration/... -run TestMigrate +``` + +For comprehensive testing strategies including single version tests, frontend comparison tests, and full pipeline testing, see the [Comprehensive Testing Strategy](#comprehensive-testing-strategy) section. + +### Step 5: Frontend-Backend Consistency Validation + +Implement different changes in the migration until frontend matches exactly with the backend side. Add as many scenarios as possible to ensure comprehensive coverage of all use cases. + +For detailed testing strategies including single version tests, frontend comparison tests, and full pipeline testing, see the [Comprehensive Testing Strategy](#comprehensive-testing-strategy) section. + +#### Test Scenarios to Cover + +**Panel-Level Changes:** +- Panels with different types (graph, table, stat, etc.) +- Panels with nested panels (rows) +- Panels with various property combinations +- Panels with missing or null properties +- Panels with deprecated properties + +**Dashboard-Level Changes:** +- Dashboard properties (title, tags, time, etc.) +- Templating variables (query, textbox, custom, etc.) +- Annotations and links +- Time picker settings +- Refresh settings + +**Edge Cases:** +- Empty dashboards +- Dashboards with only rows +- Dashboards with mixed panel types +- Dashboards with complex nested structures +- Dashboards with invalid or malformed data + +**Property Transformations:** +- Property renaming scenarios +- Property type conversions +- Property value transformations +- Property removal scenarios +- Property addition scenarios + +#### Validation Process + +1. **Create comprehensive test data** covering all scenarios +2. **Run backend migration** and capture output +3. **Run frontend migration** and capture output +4. **Compare outputs** using strict equality checks +5. **Iterate on migration logic** until outputs match exactly +6. **Add more test cases** for any discrepancies found + +#### Test Data Requirements + +Create multiple test files for each migration: +- `v{N}.basic.{name}.json` - Basic functionality +- `v{N}.edge_cases.{name}.json` - Edge cases and error conditions +- `v{N}.complex.{name}.json` - Complex dashboard structures +- `v{N}.property_changes.{name}.json` - Property transformation scenarios + +### Step 6: Unit Testing + +Add comprehensive unit tests for both backend and frontend migrations to ensure the correct logic is implemented. + +#### Backend Unit Tests + +Create unit tests in `v{N}_test.go`: + +```go +package schemaversion_test + +import ( + "context" + "testing" + "github.com/stretchr/testify/require" + "github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" +) + +func TestV{N}(t *testing.T) { + tests := []struct { + name string + input map[string]interface{} + expected map[string]interface{} + }{ + { + name: "basic property migration", + input: map[string]interface{}{ + "schemaVersion": {N-1}, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "graph", + "oldProperty": "value", + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": {N}, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "graph", + "newProperty": "value", + }, + }, + }, + }, + { + name: "edge case - missing property", + input: map[string]interface{}{ + "schemaVersion": {N-1}, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "graph", + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": {N}, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "graph", + }, + }, + }, + }, + // Add more test cases... + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := schemaversion.V{N}(context.Background(), tt.input) + require.NoError(t, err) + require.Equal(t, tt.expected, tt.input) + }) + } +} +``` + +#### Frontend Unit Tests + +Create unit tests in `DashboardMigrator.test.ts`: + +```typescript +describe('V{N} Migration', () => { + it('should migrate basic property changes', () => { + const dashboard = { + schemaVersion: {N-1}, + panels: [ + { + id: 1, + type: 'graph', + oldProperty: 'value' + } + ] + }; + + const model = new DashboardModel(dashboard); + expect(model.schemaVersion).toBe({N}); + expect(model.panels[0].newProperty).toBe('value'); + expect(model.panels[0].oldProperty).toBeUndefined(); + }); + + it('should handle edge cases correctly', () => { + const dashboard = { + schemaVersion: {N-1}, + panels: [] + }; + + const model = new DashboardModel(dashboard); + expect(model.schemaVersion).toBe({N}); + expect(model.panels).toEqual([]); + }); + + // Add more test cases... +}); +``` + + +#### Running Unit Tests + +```bash +# Backend unit tests +go test ./apps/dashboard/pkg/migration/schemaversion/... -run TestV{N} + +# Frontend unit tests +yarn test DashboardMigrator.test.ts -t "V{N}" + +# Integration tests +go test ./apps/dashboard/pkg/migration/... -run TestMigrate +yarn test DashboardMigratorToBackend.test.ts +``` + + + +## Utility Functions + +### Type Conversion +```go +// Convert various types to float64 +func ConvertToFloat(value interface{}) (float64, bool) { + // Implementation in utils.go +} + +// Get string value with default +func GetStringValue(data map[string]interface{}, key string, defaultValue string) string { + // Implementation in utils.go +} + +// Get boolean value with default +func GetBoolValue(data map[string]interface{}, key string) bool { + // Implementation in utils.go +} +``` + +### Data Access +```go +// Safe property access +if value, ok := panel["property"].(string); ok { + // Use value +} + +// Safe array access +if panels, ok := dashboard["panels"].([]interface{}); ok { + // Process panels +} +``` + +## Common Pitfalls + +### 1. Type Assertions +```go +// ❌ Wrong - can panic +value := panel["property"].(string) + +// ✅ Correct - safe type assertion +if value, ok := panel["property"].(string); ok { + // Use value +} +``` + +### 2. Null Handling +```go +// ❌ Wrong - removes intentional nulls +if value == nil { + delete(panel, "property") +} + +// ✅ Correct - preserve intentional nulls +if value == nil && !isIntentionallyNull(panel, "property") { + delete(panel, "property") +} +``` + +### 3. Nested Panel Processing +```go +// ❌ Wrong - misses nested panels +for _, panel := range panels { + migratePanel(panel) +} + +// ✅ Correct - handles nested panels +for _, panel := range panels { + migratePanelRecursively(panel) +} +``` + +## Migration Beyond v42 + +**Important**: Schema version 42 is the final version for the v1 dashboard API. For new migrations beyond v42: + +1. **Backend**: Continue adding schema versions in Go +2. **Frontend**: Use panel-specific migration handlers instead of schema versions +3. **Justification Required**: Each new migration must be properly justified +4. **v2 Transition**: Consider the impact on the transition to schema v2 + +### When to Add Schema Migrations vs Panel Migrations + +**Add Schema Migration** when: +- Changes affect dashboard-level properties +- Cross-panel transformations are needed +- Backward compatibility requires schema version increment +- **With proper justification** about why it can't be handled as panel migration + +**Use Panel-Specific Migrations** when: +- Changes are panel-type specific +- Can be handled within individual panel plugins +- Don't require dashboard-level schema changes + +## Testing Guidelines + +### Test File Naming +- Input: `v{N}.{descriptive_name}.json` +- Output: `v{N}.{descriptive_name}.v42.json` + +### Test Data Requirements +- Input schema version should be `{N-1}` +- Include comprehensive test cases +- Test edge cases and error conditions +- Use realistic dashboard data + +### Running Tests +```bash +# Run all migration tests +go test ./apps/dashboard/pkg/migration/... + +# Run specific test +go test ./apps/dashboard/pkg/migration/... -run TestMigrate + +# Run with verbose output +go test ./apps/dashboard/pkg/migration/... -v +``` + +## Comprehensive Testing Strategy + +### Backend Migration Tests + +The backend migration tests validate schema version migrations and API conversions: + +- **Schema migration tests**: Test individual schema version upgrades (v14→v15, v15→v16, etc.) +- **Conversion tests**: Test API version conversions with automatic metrics instrumentation +- **Test data**: Uses curated test files from `testdata/input/` covering schema versions 14-42 +- **Metrics validation**: Tests verify that conversion metrics are properly recorded + +**Test execution:** +```bash +# All backend migration tests +go test ./apps/dashboard/pkg/migration/... -v + +# Schema migration tests only +go test ./apps/dashboard/pkg/migration/ -v + +# API conversion tests with metrics +go test ./apps/dashboard/pkg/migration/conversion/... -v +``` + +### Frontend Migration Comparison Tests + +The frontend migration comparison tests validate that backend and frontend migration logic produce consistent results: + +- **Test methodology**: Compares backend vs frontend migration outputs through DashboardModel integration +- **Dataset coverage**: Tests run against 42+ curated test files spanning schema versions 14-42 +- **Test location**: `public/app/features/dashboard/state/DashboardMigratorToBackend.test.ts` +- **Test data**: Located in `apps/dashboard/pkg/migration/testdata/input/` and `testdata/output/` + +**Test execution:** +```bash +# Frontend migration comparison tests +yarn test DashboardMigratorToBackend.test.ts +``` + +**Test approach:** +- **Frontend path**: `jsonInput → DashboardModel → DashboardMigrator → getSaveModelClone()` +- **Backend path**: `jsonInput → Backend Migration → backendOutput → DashboardModel → getSaveModelClone()` +- **Comparison**: Direct comparison of final migrated states from both paths + +### Single Version Migration Testing + +The system supports both single version and full pipeline testing: + +- **Single version tests**: Test individual migrations (v15 → v16) +- **Full pipeline tests**: Test complete migration (v15 → v42) +- **Test directories**: `single_version/` vs `latest_version/` +- **Coverage**: Comprehensive testing of all migration paths + +**Test execution:** +```bash +# Single version migration tests +go test ./apps/dashboard/pkg/migration/... -run TestMigrateSingleVersion + +# Full pipeline tests +go test ./apps/dashboard/pkg/migration/... -run TestMigrate +``` + +## Resources + +- [Migration Architecture Documentation](../../../docs/migration-architecture.md) +- [Frontend Migration Guide](../../../../public/app/features/dashboard/state/README.md) +- [Test Data Examples](../testdata/input/) +- [Existing Migration Examples](./) + +## Getting Help + +- Review existing migrations for patterns +- Check test data for examples +- Consult the team for complex migrations +- Use the migration test utilities for common operations diff --git a/apps/dashboard/pkg/migration/schemaversion/angular_migration.go b/apps/dashboard/pkg/migration/schemaversion/angular_migration.go deleted file mode 100644 index 7dee79d8c29..00000000000 --- a/apps/dashboard/pkg/migration/schemaversion/angular_migration.go +++ /dev/null @@ -1,89 +0,0 @@ -package schemaversion - -var notPersistedProperties = []string{ - "events", - "isViewing", - "isEditing", - "isInView", - "hasRefreshed", - "cachedPluginOptions", - "plugin", - "queryRunner", - "replaceVariables", - "configRev", - "hasSavedPanelEditChange", - "getDisplayTitle", - "dataSupport", - "key", - "isNew", - "refreshWhenInView", -} - -var mustKeepProperties = []string{ - "id", - "gridPos", - "type", - "title", - "scopedVars", - "repeat", - "repeatPanelId", - "repeatDirection", - "repeatedByRow", - "minSpan", - "collapsed", - "panels", - "targets", - "datasource", - "timeFrom", - "timeShift", - "hideTimeOverride", - "description", - "links", - "fullscreen", - "isEditing", - "isViewing", - "hasRefreshed", - "events", - "cacheTimeout", - "queryCachingTTL", - "cachedPluginOptions", - "transparent", - "pluginVersion", - "queryRunner", - "transformations", - "fieldConfig", - "maxDataPoints", - "interval", - "replaceVariables", - "libraryPanel", - "getDisplayTitle", - "configRev", - "key", -} - -// getOptionsToRemember returns a map of panel properties that should be remembered -// during panel type changes, excluding notPersistedProperties and mustKeepProperties -func getOptionsToRemember(panel map[string]interface{}) map[string]interface{} { - // Create sets for faster lookup - notPersistedSet := make(map[string]bool) - for _, prop := range notPersistedProperties { - notPersistedSet[prop] = true - } - - mustKeepSet := make(map[string]bool) - for _, prop := range mustKeepProperties { - mustKeepSet[prop] = true - } - - // Filter the panel properties - result := make(map[string]interface{}) - for key, value := range panel { - // Skip properties that are in notPersistedProperties or mustKeepProperties - if notPersistedSet[key] || mustKeepSet[key] { - continue - } - result[key] = value - } - - return result -} diff --git a/apps/dashboard/pkg/migration/schemaversion/migrations.go b/apps/dashboard/pkg/migration/schemaversion/migrations.go index e8d50a9aecb..97921173fbf 100644 --- a/apps/dashboard/pkg/migration/schemaversion/migrations.go +++ b/apps/dashboard/pkg/migration/schemaversion/migrations.go @@ -7,11 +7,8 @@ import ( ) const ( - MIN_VERSION = 13 + MIN_VERSION = 6 LATEST_VERSION = 42 - - // The pluginVersion to set after simulating auto-migrate for angular panels - pluginVersionForAutoMigrate = "12.1.0" ) type SchemaVersionMigrationFunc func(context.Context, map[string]interface{}) error @@ -38,6 +35,13 @@ type PanelPluginInfo struct { func GetMigrations(dsInfoProvider DataSourceInfoProvider) map[int]SchemaVersionMigrationFunc { return map[int]SchemaVersionMigrationFunc{ + 7: V7, + 8: V8, + 9: V9, + 10: V10, + 11: V11, + 12: V12, + 13: V13, 14: V14, 15: V15, 16: V16, diff --git a/apps/dashboard/pkg/migration/schemaversion/v10.go b/apps/dashboard/pkg/migration/schemaversion/v10.go new file mode 100644 index 00000000000..6b218460aac --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v10.go @@ -0,0 +1,97 @@ +package schemaversion + +import "context" + +// V10 migration removes the first threshold value from table panel styles when they have 3 or more thresholds. +// This migration aligns with the frontend schema version 10 changes that addressed aliasYAxis changes +// specifically for table panels with threshold configurations. +// +// Background: +// In earlier versions, table panels stored threshold values as arrays with the first element representing +// a baseline value that was not actually used in threshold calculations. This migration removes that +// unused first element to clean up the data structure. +// +// Example before migration: +// { +// "schemaVersion": 9, +// "panels": [ +// { +// "type": "table", +// "styles": [ +// { +// "thresholds": ["10", "20", "30"] +// }, +// { +// "thresholds": ["100", "200", "300"] +// } +// ] +// } +// ] +// } +// +// Example after migration: +// { +// "schemaVersion": 10, +// "panels": [ +// { +// "type": "table", +// "styles": [ +// { +// "thresholds": ["20", "30"] +// }, +// { +// "thresholds": ["200", "300"] +// } +// ] +// } +// ] +// } + +func V10(_ context.Context, dashboard map[string]interface{}) error { + dashboard["schemaVersion"] = 10 + + panels, ok := dashboard["panels"].([]interface{}) + if !ok { + return nil + } + + for _, p := range panels { + panel, ok := p.(map[string]interface{}) + if !ok { + continue + } + + // Only process table panels + panelType := GetStringValue(panel, "type") + if panelType != "table" { + continue + } + + styles, ok := panel["styles"].([]interface{}) + if !ok { + continue + } + + // Process each style in the table panel + for _, s := range styles { + style, ok := s.(map[string]interface{}) + if !ok { + continue + } + + thresholds, ok := style["thresholds"].([]interface{}) + if !ok { + continue + } + + // Only modify thresholds if they have 3 or more values + if len(thresholds) >= 3 { + // Remove the first threshold value + newThresholds := thresholds[1:] + style["thresholds"] = newThresholds + } + } + } + + return nil +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v10_test.go b/apps/dashboard/pkg/migration/schemaversion/v10_test.go new file mode 100644 index 00000000000..a1bb57cf94f --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v10_test.go @@ -0,0 +1,209 @@ +package schemaversion_test + +import ( + "testing" + + "github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" +) + +func TestV10(t *testing.T) { + tests := []migrationTestCase{ + { + name: "table panel with thresholds having 3 or more values should have first threshold removed", + input: map[string]interface{}{ + "title": "V10 Table Thresholds Migration Test Dashboard", + "schemaVersion": 9, + "panels": []interface{}{ + map[string]interface{}{ + "type": "table", + "id": 1, + "styles": []interface{}{ + map[string]interface{}{ + "thresholds": []interface{}{"10", "20", "30"}, + }, + map[string]interface{}{ + "thresholds": []interface{}{"100", "200", "300"}, + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V10 Table Thresholds Migration Test Dashboard", + "schemaVersion": 10, + "panels": []interface{}{ + map[string]interface{}{ + "type": "table", + "id": 1, + "styles": []interface{}{ + map[string]interface{}{ + "thresholds": []interface{}{"20", "30"}, + }, + map[string]interface{}{ + "thresholds": []interface{}{"200", "300"}, + }, + }, + }, + }, + }, + }, + { + name: "table panel with thresholds having less than 3 values should remain unchanged", + input: map[string]interface{}{ + "title": "V10 Table Thresholds No Change Test Dashboard", + "schemaVersion": 9, + "panels": []interface{}{ + map[string]interface{}{ + "type": "table", + "id": 1, + "styles": []interface{}{ + map[string]interface{}{ + "thresholds": []interface{}{"10", "20"}, + }, + map[string]interface{}{ + "thresholds": []interface{}{"100"}, + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V10 Table Thresholds No Change Test Dashboard", + "schemaVersion": 10, + "panels": []interface{}{ + map[string]interface{}{ + "type": "table", + "id": 1, + "styles": []interface{}{ + map[string]interface{}{ + "thresholds": []interface{}{"10", "20"}, + }, + map[string]interface{}{ + "thresholds": []interface{}{"100"}, + }, + }, + }, + }, + }, + }, + { + name: "non-table panels should remain unchanged", + input: map[string]interface{}{ + "title": "V10 Non-Table Panel Test Dashboard", + "schemaVersion": 9, + "panels": []interface{}{ + map[string]interface{}{ + "type": "graph", + "id": 1, + "styles": []interface{}{ + map[string]interface{}{ + "thresholds": []interface{}{"10", "20", "30"}, + }, + }, + }, + map[string]interface{}{ + "type": "singlestat", + "id": 2, + "styles": []interface{}{ + map[string]interface{}{ + "thresholds": []interface{}{"100", "200", "300"}, + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V10 Non-Table Panel Test Dashboard", + "schemaVersion": 10, + "panels": []interface{}{ + map[string]interface{}{ + "type": "graph", + "id": 1, + "styles": []interface{}{ + map[string]interface{}{ + "thresholds": []interface{}{"10", "20", "30"}, + }, + }, + }, + map[string]interface{}{ + "type": "singlestat", + "id": 2, + "styles": []interface{}{ + map[string]interface{}{ + "thresholds": []interface{}{"100", "200", "300"}, + }, + }, + }, + }, + }, + }, + { + name: "table panel without styles should remain unchanged", + input: map[string]interface{}{ + "title": "V10 Table No Styles Test Dashboard", + "schemaVersion": 9, + "panels": []interface{}{ + map[string]interface{}{ + "type": "table", + "id": 1, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V10 Table No Styles Test Dashboard", + "schemaVersion": 10, + "panels": []interface{}{ + map[string]interface{}{ + "type": "table", + "id": 1, + }, + }, + }, + }, + { + name: "table panel with styles but no thresholds should remain unchanged", + input: map[string]interface{}{ + "title": "V10 Table No Thresholds Test Dashboard", + "schemaVersion": 9, + "panels": []interface{}{ + map[string]interface{}{ + "type": "table", + "id": 1, + "styles": []interface{}{ + map[string]interface{}{ + "colorMode": "cell", + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V10 Table No Thresholds Test Dashboard", + "schemaVersion": 10, + "panels": []interface{}{ + map[string]interface{}{ + "type": "table", + "id": 1, + "styles": []interface{}{ + map[string]interface{}{ + "colorMode": "cell", + }, + }, + }, + }, + }, + }, + { + name: "dashboard without panels should only update schema version", + input: map[string]interface{}{ + "title": "V10 No Panels Test Dashboard", + "schemaVersion": 9, + }, + expected: map[string]interface{}{ + "title": "V10 No Panels Test Dashboard", + "schemaVersion": 10, + }, + }, + } + runMigrationTests(t, tests, schemaversion.V10) +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v11.go b/apps/dashboard/pkg/migration/schemaversion/v11.go new file mode 100644 index 00000000000..a31e13315a0 --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v11.go @@ -0,0 +1,28 @@ +package schemaversion + +import "context" + +// V11 migration is a no-op migration +// It only updates the schema version to 11 +// It's created to keep the migration history consistent +// Frontend migrator doesn't have a migration for schema version 11 +// No specific migration logic is needed between schema version 10 and 11 + +// Example before migration: +// { +// "schemaVersion": 10, +// "title": "My Dashboard", +// "panels": [...] +// } + +// Example after migration: +// { +// "schemaVersion": 11, +// "title": "My Dashboard", +// "panels": [...] +// } + +func V11(_ context.Context, dashboard map[string]interface{}) error { + dashboard["schemaVersion"] = 11 + return nil +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v11_test.go b/apps/dashboard/pkg/migration/schemaversion/v11_test.go new file mode 100644 index 00000000000..d357b141639 --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v11_test.go @@ -0,0 +1,38 @@ +package schemaversion_test + +import ( + "testing" + + "github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" +) + +func TestV11(t *testing.T) { + tests := []migrationTestCase{ + { + name: "v11 no-op migration, updates schema version only", + input: map[string]interface{}{ + "title": "V11 No-Op Migration Test Dashboard", + "schemaVersion": 10, + "panels": []interface{}{ + map[string]interface{}{ + "type": "graph", + "title": "Panel remains unchanged", + "id": 1, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V11 No-Op Migration Test Dashboard", + "schemaVersion": 11, + "panels": []interface{}{ + map[string]interface{}{ + "type": "graph", + "title": "Panel remains unchanged", + "id": 1, + }, + }, + }, + }, + } + runMigrationTests(t, tests, schemaversion.V11) +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v12.go b/apps/dashboard/pkg/migration/schemaversion/v12.go new file mode 100644 index 00000000000..2eb97281937 --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v12.go @@ -0,0 +1,66 @@ +package schemaversion + +import "context" + +// V12 migrates template variables to update their refresh and hide properties. +// This migration ensures that: +// 1. Variables with refresh=true get refresh=1, and variables with refresh=false get refresh=0 +// 2. Variables with hideVariable=true get hide=2 (hide variable) +// 3. Variables with hideLabel=true get hide=1 (hide label) +// +// Example before migration: +// +// "templating": { +// "list": [ +// { "type": "query", "name": "var1", "refresh": true, "hideVariable": true }, +// { "type": "query", "name": "var2", "refresh": false, "hideLabel": true } +// ] +// } +// +// Example after migration: +// +// "templating": { +// "list": [ +// { "type": "query", "name": "var1", "refresh": 1, "hide": 2 }, +// { "type": "query", "name": "var2", "refresh": 0, "hide": 1 } +// ] +// } +func V12(_ context.Context, dashboard map[string]interface{}) error { + dashboard["schemaVersion"] = 12 + + templating, ok := dashboard["templating"].(map[string]interface{}) + if !ok { + return nil + } + + list, ok := templating["list"].([]interface{}) + if !ok { + return nil + } + + for _, v := range list { + variable, ok := v.(map[string]interface{}) + if !ok { + continue + } + + // Update refresh property + if _, hasRefresh := variable["refresh"]; hasRefresh { + if GetBoolValue(variable, "refresh") { + variable["refresh"] = 1 + } else { + variable["refresh"] = 0 + } + } + + // Update hide property based on hideVariable and hideLabel + // hideVariable takes priority over hideLabel + if GetBoolValue(variable, "hideVariable") { + variable["hide"] = 2 + } else if GetBoolValue(variable, "hideLabel") { + variable["hide"] = 1 + } + } + + return nil +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v12_test.go b/apps/dashboard/pkg/migration/schemaversion/v12_test.go new file mode 100644 index 00000000000..eef78ee450c --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v12_test.go @@ -0,0 +1,316 @@ +package schemaversion_test + +import ( + "testing" + + "github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" +) + +func TestV12(t *testing.T) { + tests := []migrationTestCase{ + { + name: "variable with refresh=true gets refresh=1", + input: map[string]interface{}{ + "title": "V12 Refresh True Test", + "schemaVersion": 11, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "refresh_true_var", + "refresh": true, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V12 Refresh True Test", + "schemaVersion": 12, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "refresh_true_var", + "refresh": 1, + }, + }, + }, + }, + }, + { + name: "variable with refresh=false gets refresh=0", + input: map[string]interface{}{ + "title": "V12 Refresh False Test", + "schemaVersion": 11, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "refresh_false_var", + "refresh": false, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V12 Refresh False Test", + "schemaVersion": 12, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "refresh_false_var", + "refresh": 0, + }, + }, + }, + }, + }, + { + name: "variable with hideVariable=true gets hide=2", + input: map[string]interface{}{ + "title": "V12 Hide Variable Test", + "schemaVersion": 11, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "hide_variable_var", + "hideVariable": true, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V12 Hide Variable Test", + "schemaVersion": 12, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "hide_variable_var", + "hideVariable": true, + "hide": 2, + }, + }, + }, + }, + }, + { + name: "variable with hideLabel=true gets hide=1", + input: map[string]interface{}{ + "title": "V12 Hide Label Test", + "schemaVersion": 11, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "hide_label_var", + "hideLabel": true, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V12 Hide Label Test", + "schemaVersion": 12, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "hide_label_var", + "hideLabel": true, + "hide": 1, + }, + }, + }, + }, + }, + { + name: "variable with both hideVariable and hideLabel prioritizes hideVariable", + input: map[string]interface{}{ + "title": "V12 Hide Priority Test", + "schemaVersion": 11, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "priority_var", + "hideVariable": true, + "hideLabel": true, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V12 Hide Priority Test", + "schemaVersion": 12, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "priority_var", + "hideVariable": true, + "hideLabel": true, + "hide": 2, + }, + }, + }, + }, + }, + { + name: "variable with no refresh or hide properties is unchanged", + input: map[string]interface{}{ + "title": "V12 No Properties Test", + "schemaVersion": 11, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "no_properties_var", + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V12 No Properties Test", + "schemaVersion": 12, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "no_properties_var", + }, + }, + }, + }, + }, + { + name: "dashboard without templating is unchanged", + input: map[string]interface{}{ + "title": "V12 No Templating Test", + "schemaVersion": 11, + }, + expected: map[string]interface{}{ + "title": "V12 No Templating Test", + "schemaVersion": 12, + }, + }, + { + name: "dashboard with empty templating list is unchanged", + input: map[string]interface{}{ + "title": "V12 Empty Templating Test", + "schemaVersion": 11, + "templating": map[string]interface{}{ + "list": []interface{}{}, + }, + }, + expected: map[string]interface{}{ + "title": "V12 Empty Templating Test", + "schemaVersion": 12, + "templating": map[string]interface{}{ + "list": []interface{}{}, + }, + }, + }, + { + name: "variables with hideVariable=false and hideLabel=false do not get hide property", + input: map[string]interface{}{ + "title": "V12 False Hide Properties Test", + "schemaVersion": 11, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "false_hide_var", + "hideVariable": false, + "hideLabel": false, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V12 False Hide Properties Test", + "schemaVersion": 12, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "false_hide_var", + "hideVariable": false, + "hideLabel": false, + }, + }, + }, + }, + }, + { + name: "variable with hideVariable=false but hideLabel=true gets hide=1", + input: map[string]interface{}{ + "title": "V12 Mixed Hide Properties Test", + "schemaVersion": 11, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "mixed_hide_var", + "hideVariable": false, + "hideLabel": true, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V12 Mixed Hide Properties Test", + "schemaVersion": 12, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "mixed_hide_var", + "hideVariable": false, + "hideLabel": true, + "hide": 1, + }, + }, + }, + }, + }, + { + name: "variable with all properties gets all migrations applied", + input: map[string]interface{}{ + "title": "V12 All Properties Test", + "schemaVersion": 11, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "all_properties_var", + "refresh": true, + "hideVariable": true, + "hideLabel": false, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V12 All Properties Test", + "schemaVersion": 12, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "type": "query", + "name": "all_properties_var", + "refresh": 1, + "hideVariable": true, + "hideLabel": false, + "hide": 2, + }, + }, + }, + }, + }, + } + + runMigrationTests(t, tests, schemaversion.V12) +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v13.go b/apps/dashboard/pkg/migration/schemaversion/v13.go new file mode 100644 index 00000000000..791c3576022 --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v13.go @@ -0,0 +1,11 @@ +package schemaversion + +import ( + "context" +) + +// V13 is a no-op migration +func V13(_ context.Context, dashboard map[string]interface{}) error { + dashboard["schemaVersion"] = 13 + return nil +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v13_test.go b/apps/dashboard/pkg/migration/schemaversion/v13_test.go new file mode 100644 index 00000000000..729bcb25120 --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v13_test.go @@ -0,0 +1,38 @@ +package schemaversion_test + +import ( + "testing" + + "github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" +) + +func TestV13(t *testing.T) { + tests := []migrationTestCase{ + { + name: "v13 no-op migration, updates schema version only", + input: map[string]interface{}{ + "title": "V13 No-Op Migration Test Dashboard", + "schemaVersion": 12, + "panels": []interface{}{ + map[string]interface{}{ + "type": "graph", + "title": "Panel remains unchanged", + "id": 1, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V13 No-Op Migration Test Dashboard", + "schemaVersion": 13, + "panels": []interface{}{ + map[string]interface{}{ + "type": "graph", + "title": "Panel remains unchanged", + "id": 1, + }, + }, + }, + }, + } + runMigrationTests(t, tests, schemaversion.V13) +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v24.go b/apps/dashboard/pkg/migration/schemaversion/v24.go index 4526e23d15e..d9b4dd59ed4 100644 --- a/apps/dashboard/pkg/migration/schemaversion/v24.go +++ b/apps/dashboard/pkg/migration/schemaversion/v24.go @@ -2,189 +2,59 @@ package schemaversion import ( "context" - "strconv" ) -// V24 migration migrates the angular table panel to the standard table panel -// In the frontend, this is an auto-migration meaning that this angular panel is always migrated to table panel. -// The backend replicates the complete frontend auto-migration logic since it cannot rely on frontend auto-migration. -// -// This migration performs: -// 1. Converts 'styles' array to 'fieldConfig' with 'defaults' and 'overrides' -// 2. Migrates thresholds and colors to new threshold format -// 3. Converts column-specific styles to field overrides -// 4. Migrates transformations from old format to new transformation system -// 5. Handles various style properties: unit, decimals, alignment, color modes, links, date formatting, hidden columns -// 6. Removes deprecated properties: styles, transform, columns +// V24 migration handles setting autoMigrateFrom +// This is a hacky way that matches frontend's logic +// For reason see https://github.com/grafana/grafana/pull/102146 +// The issue is that if panel is "table" and it has styles, it should be migrated to "table-old" -// Example 1: Basic table with defaults -// Before migration: +// Example before migration: // { -// "panels": [ -// { -// "id": 1, -// "type": "table", -// "title": "Basic Table", -// "styles": [ -// { -// "pattern": "/.*/", -// "thresholds": ["10", "20", "30"], -// "colors": ["green", "yellow", "red"], -// "unit": "bytes", -// "decimals": 2 -// } -// ], -// "targets": [{ "refId": "A" }] -// } -// ] +// "id": 4, +// "type": "table", +// "title": "Table with Timeseries to Rows Transform", +// "description": "Tests migration of timeseries_to_rows transform to seriesToRows transformation.", +// "styles": [ +// { +// "pattern": "/.*/", +// "unit": "short" +// } +// ], +// "transform": "timeseries_to_rows", +// "targets": [{ "refId": "A" }] // } // -// After migration: +// Example after migration: // { -// "panels": [ -// { -// "id": 1, -// "type": "table", -// "title": "Basic Table", -// "fieldConfig": { -// "defaults": { -// "unit": "bytes", -// "decimals": 2, -// "custom": {}, -// "thresholds": { -// "mode": "absolute", -// "steps": [ -// { "color": "green", "value": null }, -// { "color": "green", "value": 10 }, -// { "color": "yellow", "value": 20 }, -// { "color": "red", "value": 30 } -// ] -// } -// }, -// "overrides": [] -// }, -// "transformations": [], -// "targets": [{ "refId": "A" }], -// "pluginVersion": "{current_grafana_version}" -// } -// ] -// } - -// Example 2: Complex table with overrides and transformations -// Before migration: -// { -// "panels": [ -// { -// "id": 2, -// "type": "table", -// "title": "Complex Table", -// "styles": [ -// { -// "pattern": "/.*/", -// "unit": "percent", -// "align": "center", -// "colorMode": "cell" -// }, -// { -// "pattern": "Status", -// "alias": "Current Status", -// "colorMode": "value", -// "align": "left" -// }, -// { -// "pattern": "/Error.*/", -// "link": true, -// "linkUrl": "http://example.com/errors", -// "linkTooltip": "View errors", -// "linkTargetBlank": true -// }, -// { -// "pattern": "Time", -// "type": "date", -// "dateFormat": "YYYY-MM-DD HH:mm:ss", -// "alias": "Timestamp" -// }, -// { -// "pattern": "Hidden", -// "type": "hidden" -// } -// ], -// "transform": "timeseries_aggregations", -// "columns": [ -// { "value": "avg", "text": "Average" }, -// { "value": "max", "text": "Maximum" } -// ], -// "targets": [{ "refId": "A" }] -// } -// ] -// } -// -// After migration: -// { -// "panels": [ -// { -// "id": 2, -// "type": "table", -// "title": "Complex Table", -// "fieldConfig": { -// "defaults": { -// "unit": "percent", -// "custom": { -// "align": "center", -// "cellOptions": { "type": "color-background" } -// } -// }, -// "overrides": [ -// { -// "matcher": { "id": "byName", "options": "Status" }, -// "properties": [ -// { "id": "displayName", "value": "Current Status" }, -// { "id": "custom.cellOptions", "value": { "type": "color-text" } }, -// { "id": "custom.align", "value": "left" } -// ] -// }, -// { -// "matcher": { "id": "byRegexp", "options": "/Error.*/" }, -// "properties": [ -// { -// "id": "links", -// "value": [{ -// "title": "View errors", -// "url": "http://example.com/errors", -// "targetBlank": true -// }] -// } -// ] -// }, -// { -// "matcher": { "id": "byName", "options": "Time" }, -// "properties": [ -// { "id": "displayName", "value": "Timestamp" }, -// { "id": "unit", "value": "time: YYYY-MM-DD HH:mm:ss" } -// ] -// }, -// { -// "matcher": { "id": "byName", "options": "Hidden" }, -// "properties": [ -// { "id": "custom.hideFrom.viz", "value": true } -// ] -// } -// ] -// }, -// "transformations": [ -// { -// "id": "reduce", -// "options": { -// "reducers": ["mean", "max"], -// "includeTimeField": false -// } -// } -// ], -// "targets": [{ "refId": "A" }], -// "pluginVersion": "{current_grafana_version}" -// } -// ] -// } +// "autoMigrateFrom": "table-old", +// "datasource": { +// "apiVersion": "v1", +// "type": "prometheus", +// "uid": "default-ds-uid" +// }, +// "description": "Tests migration of timeseries_to_rows transform to seriesToRows transformation.", +// "id": 4, +// "styles": [ +// { +// "pattern": "/.*/", +// "unit": "short" +// } +// ], +// "targets": [ +// { +// "datasource": { +// "apiVersion": "v1", +// "type": "prometheus", +// "uid": "default-ds-uid" +// }, +// "refId": "A" +// } +// ], +// "title": "Table with Timeseries to Rows Transform", +// "transform": "timeseries_to_rows", +// "type": "table" +// } func V24(_ context.Context, dashboard map[string]interface{}) error { dashboard["schemaVersion"] = 24 @@ -211,406 +81,18 @@ func V24(_ context.Context, dashboard map[string]interface{}) error { continue } - // The grafana version that matches the hardcoded autoMigrate plugins - panelMap["pluginVersion"] = pluginVersionForAutoMigrate - err := tablePanelChangedHandler(panelMap) - if err != nil { - return err - } - } - - return nil -} - -func tablePanelChangedHandler(panel map[string]interface{}) error { - prevOptions := getOptionsToRemember(panel) - - transformations := migrateTransformations(panel, prevOptions) - - prevDefaults := findDefaultStyle(prevOptions) - defaults := migrateDefaults(prevDefaults) - - overrides := findNonDefaultStyles(prevOptions) - - if len(overrides) == 0 { - overrides = []interface{}{} - } - - // Only add transformations if they're not empty - frontend omits empty arrays - if len(transformations) > 0 { - panel["transformations"] = transformations - } - panel["fieldConfig"] = map[string]interface{}{ - "defaults": defaults, - "overrides": overrides, - } - - // Add minimal table panel options to match frontend behavior - // Frontend doesn't add default footer options, so we don't either - panel["options"] = map[string]interface{}{ - "cellHeight": "sm", - "showHeader": true, - } - - // Remove deprecated properties - delete(panel, "styles") - delete(panel, "transform") - delete(panel, "columns") - - // Remove legend property - frontend table panel migration doesn't preserve it - delete(panel, "legend") - - return nil -} - -// findDefaultStyle finds the style with pattern '/.*/' (default style) -func findDefaultStyle(prevOptions map[string]interface{}) map[string]interface{} { - if styles, ok := prevOptions["styles"].([]interface{}); ok { - for _, style := range styles { - if styleMap, ok := style.(map[string]interface{}); ok { - if pattern, ok := styleMap["pattern"].(string); ok && pattern == "/.*/" { - return styleMap - } - } - } - } - return nil -} - -// findNonDefaultStyles finds all styles that don't have pattern '/.*/' -func findNonDefaultStyles(prevOptions map[string]interface{}) []interface{} { - var overrides []interface{} - - if styles, ok := prevOptions["styles"].([]interface{}); ok { - for _, style := range styles { - if styleMap, ok := style.(map[string]interface{}); ok { - if pattern, ok := styleMap["pattern"].(string); ok && pattern != "/.*/" { - override := migrateTableStyleToOverride(styleMap) - overrides = append(overrides, override) - } - } - } - } - return overrides -} - -// migrateTransformations converts old table transformations to new format -func migrateTransformations(panel map[string]interface{}, oldOpts map[string]interface{}) []interface{} { - transformations := []interface{}{} - if existing, ok := panel["transformations"].([]interface{}); ok { - transformations = existing - } - - // Check if oldOpts has a transform that we can map - if transform, ok := oldOpts["transform"].(string); ok { - if newTransformID, exists := transformsMap[transform]; exists { - opts := map[string]interface{}{ - "reducers": []interface{}{}, - } - - // Handle timeseries_aggregations specifically - if transform == "timeseries_aggregations" { - opts["includeTimeField"] = false - - // Map columns to reducers - if columns, ok := oldOpts["columns"].([]interface{}); ok { - var reducers []interface{} - for _, column := range columns { - if columnMap, ok := column.(map[string]interface{}); ok { - if value, ok := columnMap["value"].(string); ok { - if reducer, exists := columnsMap[value]; exists { - reducers = append(reducers, reducer) - } - } - } - } - opts["reducers"] = reducers - } - } - - // Add the transformation - transformation := map[string]interface{}{ - "id": newTransformID, - "options": opts, - } - transformations = append(transformations, transformation) - } - } - - return transformations -} - -// transformsMap maps old transform names to new transformation IDs -var transformsMap = map[string]string{ - "timeseries_to_rows": "seriesToRows", - "timeseries_to_columns": "seriesToColumns", - "timeseries_aggregations": "reduce", - "table": "merge", -} - -// columnsMap maps old column values to new reducer names -var columnsMap = map[string]string{ - "avg": "mean", - "min": "min", - "max": "max", - "total": "sum", - "current": "lastNotNull", - "count": "count", -} - -// migrateTableStyleToOverride converts a table style to a field config override -func migrateTableStyleToOverride(style map[string]interface{}) map[string]interface{} { - pattern, _ := style["pattern"].(string) - - // Determine field matcher ID based on pattern - fieldMatcherID := "byName" - if pattern != "" && len(pattern) >= 2 && pattern[0] == '/' && pattern[len(pattern)-1] == '/' { - fieldMatcherID = "byRegexp" - } - - override := map[string]interface{}{ - "matcher": map[string]interface{}{ - "id": fieldMatcherID, - "options": pattern, - }, - "properties": []interface{}{}, - } - - properties := override["properties"].([]interface{}) - - // Add display name - if alias, ok := style["alias"].(string); ok && alias != "" { - properties = append(properties, map[string]interface{}{ - "id": "displayName", - "value": alias, - }) - } - - // Add unit - if unit, ok := style["unit"].(string); ok && unit != "" { - properties = append(properties, map[string]interface{}{ - "id": "unit", - "value": unit, - }) - } - - // Add decimals - if decimals := GetIntValue(style, "decimals", -1); decimals != -1 { - properties = append(properties, map[string]interface{}{ - "id": "decimals", - "value": decimals, - }) - } - - // Handle date type - if styleType, ok := style["type"].(string); ok && styleType == "date" { - if dateFormat, ok := style["dateFormat"].(string); ok { - properties = append(properties, map[string]interface{}{ - "id": "unit", - "value": "time: " + dateFormat, - }) - } - } - - // Handle hidden type - if styleType, ok := style["type"].(string); ok && styleType == "hidden" { - properties = append(properties, map[string]interface{}{ - "id": "custom.hideFrom.viz", - "value": true, - }) - } - - // Handle links - if link, ok := style["link"].(bool); ok && link { - linkTooltip, _ := style["linkTooltip"].(string) - linkUrl, _ := style["linkUrl"].(string) - linkTargetBlank, _ := style["linkTargetBlank"].(bool) - - properties = append(properties, map[string]interface{}{ - "id": "links", - "value": []interface{}{ - map[string]interface{}{ - "title": linkTooltip, - "url": linkUrl, - "targetBlank": linkTargetBlank, - }, - }, - }) - } - - // Handle color mode - if colorMode, ok := style["colorMode"].(string); ok && colorMode != "" { - if newColorMode, exists := colorModeMap[colorMode]; exists { - properties = append(properties, map[string]interface{}{ - "id": "custom.cellOptions", - "value": map[string]interface{}{ - "type": newColorMode, - }, - }) - } - } - - // Handle alignment - if align, ok := style["align"].(string); ok && align != "" { - var alignValue interface{} - if align == "auto" { - alignValue = nil // Frontend sets to null and filters it out + var currentType string + if wasAngularTable { + currentType = "table-old" } else { - alignValue = align + currentType = "table" } - properties = append(properties, map[string]interface{}{ - "id": "custom.align", - "value": alignValue, - }) - } - // Handle thresholds - if thresholds, ok := style["thresholds"].([]interface{}); ok && len(thresholds) > 0 { - if colors, ok := style["colors"].([]interface{}); ok && len(colors) > 0 { - steps := generateThresholds(thresholds, colors) - properties = append(properties, map[string]interface{}{ - "id": "thresholds", - "value": map[string]interface{}{ - "mode": "absolute", - "steps": steps, - }, - }) + if currentType == "table-old" { + panelMap["autoMigrateFrom"] = "table-old" + panelMap["type"] = "table" } } - override["properties"] = properties - return override -} - -// migrateDefaults converts default table styles to field config defaults -func migrateDefaults(prevDefaults map[string]interface{}) map[string]interface{} { - defaults := map[string]interface{}{ - "custom": map[string]interface{}{ - "align": "auto", - "cellOptions": map[string]interface{}{ - "type": "auto", - }, - "inspect": false, - "footer": map[string]interface{}{ - "reducers": []interface{}{}, - }, - }, - "mappings": []interface{}{}, - } - - // Add default thresholds for all table panels to match frontend behavior - // The frontend applies the table panel's default field config which includes thresholds - hasThresholds := false - if prevDefaults != nil { - if thresholds, ok := prevDefaults["thresholds"].([]interface{}); ok && len(thresholds) > 0 { - hasThresholds = true - } - } - - // Add default thresholds for all table panels (when prevDefaults exists) without existing thresholds - if !hasThresholds { - defaults["thresholds"] = map[string]interface{}{ - "mode": "absolute", - "steps": []interface{}{ - map[string]interface{}{"color": "green", "value": (*float64)(nil)}, - map[string]interface{}{"color": "red", "value": 80}, - }, - } - } - - if prevDefaults == nil { - return defaults - } - - if unit := GetStringValue(prevDefaults, "unit"); unit != "" { - defaults["unit"] = unit - } - - if decimals := GetIntValue(prevDefaults, "decimals", -1); decimals != -1 { - defaults["decimals"] = decimals - } - - if alias, ok := prevDefaults["alias"].(string); ok { - defaults["displayName"] = alias - } - - if align, ok := prevDefaults["align"].(string); ok && align != "" { - var alignValue interface{} - if align == "auto" { - alignValue = nil // Frontend sets to null and filters it out - } else { - alignValue = align - } - defaults["custom"].(map[string]interface{})["align"] = alignValue - } - - if thresholds, ok := prevDefaults["thresholds"].([]interface{}); ok && len(thresholds) > 0 { - if colors, ok := prevDefaults["colors"].([]interface{}); ok && len(colors) > 0 { - steps := generateThresholds(thresholds, colors) - defaults["thresholds"] = map[string]interface{}{ - "mode": "absolute", - "steps": steps, - } - } - } - - if colorMode, ok := prevDefaults["colorMode"].(string); ok && colorMode != "" { - if newColorMode, exists := colorModeMap[colorMode]; exists { - defaults["custom"].(map[string]interface{})["cellOptions"] = map[string]interface{}{ - "type": newColorMode, - } - } - } - - return defaults -} - -func generateThresholds(thresholds []interface{}, colors []interface{}) []interface{} { - steps := []interface{}{} - - // Add the base step (equivalent to -Infinity) - var baseColor interface{} = "red" // default fallback - if len(colors) > 0 && colors[0] != nil { - baseColor = colors[0] - } - - steps = append(steps, map[string]interface{}{ - "color": baseColor, - "value": (*float64)(nil), - }) - - // Add threshold steps - for i, threshold := range thresholds { - var value float64 - switch v := threshold.(type) { - case string: - if parsed, err := strconv.ParseFloat(v, 64); err == nil { - value = parsed - } - case float64: - value = v - case int: - value = float64(v) - } - - step := map[string]interface{}{ - "value": value, - } - - // Only add color if there's a corresponding color in the colors array - // This matches the frontend behavior where colors[idx] might be undefined - if i+1 < len(colors) && colors[i+1] != nil { - step["color"] = colors[i+1] - } - - steps = append(steps, step) - } - - return steps -} - -var colorModeMap = map[string]string{ - "cell": "color-background", - "row": "color-background", - "value": "color-text", + return nil } diff --git a/apps/dashboard/pkg/migration/schemaversion/v24_test.go b/apps/dashboard/pkg/migration/schemaversion/v24_test.go index 45bbf0b1df2..9abec040221 100644 --- a/apps/dashboard/pkg/migration/schemaversion/v24_test.go +++ b/apps/dashboard/pkg/migration/schemaversion/v24_test.go @@ -44,6 +44,16 @@ func TestV24TablePanelMigration(t *testing.T) { }, description: "V24 migration should not add empty transformations arrays to table panels", }, + { + name: "migrate_table_old_to_table", + input: map[string]interface{}{ + "type": "table-old", + }, + expected: map[string]interface{}{ + "type": "table", + }, + description: "V24 migration should migrate table-old to table", + }, } for _, tt := range tests { diff --git a/apps/dashboard/pkg/migration/schemaversion/v28.go b/apps/dashboard/pkg/migration/schemaversion/v28.go index 2144ae65959..d1f3bab6e37 100644 --- a/apps/dashboard/pkg/migration/schemaversion/v28.go +++ b/apps/dashboard/pkg/migration/schemaversion/v28.go @@ -2,61 +2,32 @@ package schemaversion import ( "context" - "fmt" - "strconv" - "strings" ) -// V28 migrates singlestat panels to stat/gauge panels and removes deprecated variable properties. -// -// The migration performs two main tasks: -// 1. Migrates singlestat panels to either stat or gauge panels based on their configuration -// 2. Removes deprecated variable properties (tags, tagsQuery, tagValuesQuery, useTags) -// -// The migration includes comprehensive logic from the frontend: -// - Panel type migration (singlestat -> stat/gauge) -// - Field config migration with thresholds, mappings, and display options -// - Options migration including reduceOptions, orientation, and other panel-specific settings -// - Support for both angular singlestat and grafana-singlestat-panel migrations +// V28 removes deprecated variable properties (tags, tagsQuery, tagValuesQuery, useTags) // // Example before migration: // -// "panels": [ -// { -// "type": "singlestat", -// "gauge": { "show": true }, -// "targets": [{ "refId": "A" }] -// } -// ], -// "templating": { -// "list": [ -// { "name": "var1", "tags": ["tag1"], "tagsQuery": "query", "tagValuesQuery": "values", "useTags": true } -// ] +// { +// "templating": { +// "list": [ +// { "name": "var1", "tags": ["tag1"], "tagsQuery": "query", "tagValuesQuery": "values", "useTags": true } +// ] +// } // } // // Example after migration: // -// "panels": [ -// { -// "type": "gauge", -// "targets": [{ "refId": "A" }] -// } -// ], -// "templating": { -// "list": [ -// { "name": "var1" } -// ] +// { +// "templating": { +// "list": [ +// { "name": "var1" } +// ] +// } // } func V28(_ context.Context, dashboard map[string]interface{}) error { dashboard["schemaVersion"] = 28 - // Migrate singlestat panels - if panels, ok := dashboard["panels"].([]interface{}); ok { - if err := processPanels(panels); err != nil { - return err - } - } - // Remove deprecated variable properties if templating, ok := dashboard["templating"].(map[string]interface{}); ok { if list, ok := templating["list"].([]interface{}); ok { @@ -71,751 +42,6 @@ func V28(_ context.Context, dashboard map[string]interface{}) error { return nil } -func processPanels(panels []interface{}) error { - for _, panel := range panels { - p, ok := panel.(map[string]interface{}) - if !ok { - continue - } - - // Process nested panels if this is a row panel - if p["type"] == "row" { - if nestedPanels, ok := p["panels"].([]interface{}); ok { - if err := processPanels(nestedPanels); err != nil { - return err - } - } - continue - } - - // Migrate singlestat panels (including those already auto-migrated to stat) - if p["type"] == "singlestat" || p["type"] == "grafana-singlestat-panel" || - p["autoMigrateFrom"] == "singlestat" || p["autoMigrateFrom"] == "grafana-singlestat-panel" { - if err := migrateSinglestatPanel(p); err != nil { - return err - } - } - - // Note: Panel defaults (including options object) are already applied - // by applyPanelDefaults() in the main migration flow for ALL panels - // No need for stat-specific normalization - } - - return nil -} - -func migrateSinglestatPanel(panel map[string]interface{}) error { - targetType := "stat" - - // NOTE: The legacy types "singlestat" and "gauge" are both angular only - // This are not supported by any version that could run this migration, so there is - // no need to maintain a distinction or fallback to the non-stat version - - // NOTE: DashboardMigrator's migrateSinglestat function has some logic that never gets called - // migrateSinglestat will only run if (panel.type === 'singlestat') - // but this will not be the case because PanelModel runs restoreModel in the constructor - // and since singlestat is in the autoMigrateAngular map, it will be migrated to stat, - // and therefore migrateSinglestat will never run so this logic inside of it will never apply - // if ((panel as any).gauge?.show) { - // gaugePanelPlugin.meta = config.panels['gauge'] - // panel.changePlugin(gaugePanelPlugin) - - // Store original type for migration context (only for stat/gauge migration) - // Set autoMigrateFrom to track the original type for proper migration logic - originalType := panel["type"].(string) - // Only set autoMigrateFrom if it doesn't already exist (preserve frontend defaults) - if _, exists := panel["autoMigrateFrom"]; !exists { - panel["autoMigrateFrom"] = originalType - } - panel["type"] = targetType - panel["pluginVersion"] = pluginVersionForAutoMigrate - - // Migrate panel options and field config - migrateSinglestatOptions(panel, originalType) - - return nil -} - -// migrateSinglestatOptions handles the complete migration of singlestat panel options and field config -func migrateSinglestatOptions(panel map[string]interface{}, originalType string) { - // Preserve important panel-level properties that should not be removed - // These properties are preserved by the frontend's getSaveModel() method - var maxDataPoints interface{} - if mdp, exists := panel["maxDataPoints"]; exists { - maxDataPoints = mdp - } - - // Initialize field config if not present - if panel["fieldConfig"] == nil { - panel["fieldConfig"] = map[string]interface{}{ - "defaults": map[string]interface{}{}, - "overrides": []interface{}{}, - } - } - - fieldConfig := panel["fieldConfig"].(map[string]interface{}) - defaults := fieldConfig["defaults"].(map[string]interface{}) - - // Migrate from angular singlestat configuration using appropriate strategy - // Use autoMigrateFrom if available, otherwise use originalType - migrationType := originalType - if autoMigrateFrom, exists := panel["autoMigrateFrom"].(string); exists { - migrationType = autoMigrateFrom - } - - if migrationType == "grafana-singlestat-panel" { - migrateGrafanaSinglestatPanel(panel, defaults) - } else { - migratetSinglestat(panel, defaults) - } - - // Apply shared migration logic - applySharedSinglestatMigration(defaults) - - // Apply complete stat panel defaults (matches frontend getPanelOptionsWithDefaults) - // The frontend applies these defaults after migration via applyPluginOptionDefaults - applyCompleteStatPanelDefaults(panel) - - // Create proper fieldConfig structure from defaults - createFieldConfigFromDefaults(panel, defaults) - - // Restore preserved panel-level properties - if maxDataPoints != nil { - panel["maxDataPoints"] = maxDataPoints - } - - // Clean up old angular properties after migration - cleanupAngularProperties(panel) -} - -// getDefaultStatOptions returns the default options structure for stat panels -// This matches the frontend's stat panel defaultOptions exactly -func getDefaultStatOptions() map[string]interface{} { - // For now, return the explicit defaults until we integrate the centralized system - return map[string]interface{}{ - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "percentChangeColorMode": "standard", - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true, - "reduceOptions": map[string]interface{}{ - "calcs": []string{"lastNotNull"}, // Matches frontend: ReducerID.lastNotNull - "fields": "", - "values": false, - }, - "orientation": "auto", - } -} - -// migratetSinglestat handles explicit migration from 'singlestat' panels -// Based on frontend migrateFromAngularSinglestat function -func migratetSinglestat(panel map[string]interface{}, defaults map[string]interface{}) { - angularOpts := extractAngularOptions(panel) - - // Extract valueName for reducer mapping (matches frontend migrateFromAngularSinglestat) - var valueName string - if vn, ok := angularOpts["valueName"].(string); ok { - valueName = vn - } - - // Set calcs based on valueName (matches frontend: calcs: [reducer ? reducer.id : ReducerID.mean]) - var calcs []string - if reducer := getReducerForValueName(valueName); reducer != "" { - calcs = []string{reducer} - } else { - // Use mean as fallback (matches frontend migrateFromAngularSinglestat: ReducerID.mean) - calcs = []string{"mean"} - } - - // Create options exactly like frontend migrateFromAngularSinglestat - options := map[string]interface{}{ - "reduceOptions": map[string]interface{}{ - "calcs": calcs, - "fields": "", - "values": false, - }, - "orientation": "horizontal", // Matches frontend migrateFromAngularSinglestat: VizOrientation.Horizontal - } - - // Migrate thresholds FIRST (consolidated: both panel types create DEFAULT_THRESHOLDS for empty strings) - migrateThresholds(angularOpts, defaults) - - // If no thresholds were set from angular migration, add default stat panel thresholds - // This matches the behavior of frontend pluginLoaded which adds default thresholds - if _, hasThresholds := defaults["thresholds"]; !hasThresholds { - defaults["thresholds"] = map[string]interface{}{ - "mode": "absolute", - "steps": []interface{}{ - map[string]interface{}{ - "color": "green", - "value": (*float64)(nil), - }, - map[string]interface{}{ - "color": "red", - "value": 80, - }, - }, - } - } - - // Apply common angular option migrations (value mappings can now use threshold colors) - applyCommonAngularMigration(panel, defaults, options, angularOpts) - - // Merge new options with existing panel options to preserve properties like maxDataPoints - if existingOptions, exists := panel["options"].(map[string]interface{}); exists { - for key, value := range options { - existingOptions[key] = value - } - } else { - panel["options"] = options - } -} - -// migrateGrafanaSinglestatPanel handles auto-migration from 'grafana-singlestat-panel' -// Uses the same migration logic as singlestat panels since the frontend applies -// migrateFromAngularSinglestat to both panel types. -func migrateGrafanaSinglestatPanel(panel map[string]interface{}, defaults map[string]interface{}) { - migratetSinglestat(panel, defaults) -} - -// migrateThresholds handles threshold migration for both singlestat panel types -// Both panel types now create DEFAULT_THRESHOLDS when threshold string is empty (consolidated behavior) -func migrateThresholds(angularOpts map[string]interface{}, defaults map[string]interface{}) { - if thresholds, ok := angularOpts["thresholds"].(string); ok { - if colors, ok := angularOpts["colors"].([]interface{}); ok { - if thresholds != "" { - // Non-empty thresholds: use normal migration - migrateThresholdsAndColors(defaults, thresholds, colors) - } else { - // Empty thresholds: use frontend DEFAULT_THRESHOLDS fallback (both panel types) - defaults["thresholds"] = map[string]interface{}{ - "mode": "absolute", - "steps": []interface{}{ - map[string]interface{}{ - "color": "green", - "value": (*float64)(nil), // Use pointer to ensure field is present in JSON - }, - map[string]interface{}{ - "color": "red", - "value": 80, - }, - }, - } - } - } - } -} - -// applyCommonAngularMigration applies migrations common to both singlestat types -func applyCommonAngularMigration(panel map[string]interface{}, defaults map[string]interface{}, options map[string]interface{}, angularOpts map[string]interface{}) { - // Migrate table column - // Based on sharedSingleStatPanelChangedHandler line ~125: options.reduceOptions.fields = `/^${prevPanel.tableColumn}$/` - if tableColumn, ok := angularOpts["tableColumn"].(string); ok && tableColumn != "" { - options["reduceOptions"].(map[string]interface{})["fields"] = "/^" + tableColumn + "$/" - } - - // Migrate unit from format property (matches frontend sharedSingleStatPanelChangedHandler) - if format, ok := angularOpts["format"].(string); ok { - defaults["unit"] = format - } - - // Migrate decimals - if decimals, ok := angularOpts["decimals"]; ok { - defaults["decimals"] = decimals - } - - // Note: Frontend migrateFromAngularSinglestat does migrate nullPointMode to nullValueMode - // but the frontend's getSaveModel() method removes it, so we don't add it here - // if nullPointMode, ok := angularOpts["nullPointMode"]; ok { - // defaults["nullValueMode"] = nullPointMode - // } - - // Migrate null text - if nullText, ok := angularOpts["nullText"].(string); ok { - defaults["noValue"] = nullText - } - - // Migrate value mappings (thresholds should already be migrated) - valueMaps, _ := angularOpts["valueMaps"].([]interface{}) - migrateValueMappings(angularOpts, defaults, valueMaps) - - // Migrate sparkline configuration - // Based on statPanelChangedHandler lines ~20-23: sparkline migration logic - if sparkline, ok := angularOpts["sparkline"].(map[string]interface{}); ok { - if show, ok := sparkline["show"].(bool); ok && show { - options["graphMode"] = "area" - } else { - options["graphMode"] = "none" - } - } else { - // Default to no graph mode if no sparkline configuration - options["graphMode"] = "none" - } - - // Migrate color configuration - // Based on statPanelChangedHandler lines ~25-38: colorBackground and colorValue migration - colorMode := determineColorMode(angularOpts) - options["colorMode"] = colorMode - - // Sparkline color migration only happens when colorMode is "none" - if colorMode == "none" { - migrateSparklineColor(angularOpts, defaults, options) - } - - // Migrate text mode - // Based on statPanelChangedHandler lines ~45-47: valueName === 'name' migration - if valueName, ok := angularOpts["valueName"].(string); ok && valueName == "name" { - options["textMode"] = "name" - } - - if angularOpts["gauge"] != nil && angularOpts["gauge"].(map[string]interface{})["show"] == true { - defaults["min"] = angularOpts["gauge"].(map[string]interface{})["minValue"] - defaults["max"] = angularOpts["gauge"].(map[string]interface{})["maxValue"] - } -} - -// applyCompleteStatPanelDefaults applies the complete stat panel defaults -// This matches the frontend's getPanelOptionsWithDefaults behavior after migration -func applyCompleteStatPanelDefaults(panel map[string]interface{}) { - // Get or create options object - options, exists := panel["options"].(map[string]interface{}) - if !exists { - options = map[string]interface{}{} - panel["options"] = options - } - - defaultOptions := getDefaultStatOptions() - - // Merge defaults with existing options, but don't override existing values - // This matches the frontend's getPanelOptionsWithDefaults behavior - for key, defaultValue := range defaultOptions { - if _, exists := options[key]; !exists { - options[key] = defaultValue - } - } -} - -// applySharedSinglestatMigration applies shared migration logic for all singlestat panels -// Based on sharedSingleStatMigrationHandler in packages/grafana-ui/src/components/SingleStatShared/SingleStatBaseOptions.ts -func applySharedSinglestatMigration(defaults map[string]interface{}) { - // Ensure thresholds have proper structure - if thresholds, ok := defaults["thresholds"].(map[string]interface{}); ok { - if steps, ok := thresholds["steps"].([]interface{}); ok { - // Ensure first threshold is -Infinity (represented as null in JSON) - if len(steps) > 0 { - if firstStep, ok := steps[0].(map[string]interface{}); ok { - if firstStep["value"] == nil { - firstStep["value"] = nil // Use null instead of -math.Inf(1) - } - } - } - } - } - - // Handle percent/percentunit units - // Based on sharedSingleStatMigrationHandler lines ~280-300: percent/percentunit min/max handling - if unit, ok := defaults["unit"].(string); ok { - switch unit { - case "percent": - if defaults["min"] == nil { - defaults["min"] = 0 - } - if defaults["max"] == nil { - defaults["max"] = 100 - } - case "percentunit": - if defaults["min"] == nil { - defaults["min"] = 0 - } - if defaults["max"] == nil { - defaults["max"] = 1 - } - } - } -} - -// Helper functions - -func extractAngularOptions(panel map[string]interface{}) map[string]interface{} { - // Some panels might have angular options directly in the root - // Check for common angular properties - angularProps := []string{ - "valueName", "tableColumn", "format", "decimals", "nullPointMode", "nullText", - "thresholds", "colors", "valueMaps", "gauge", "sparkline", "colorBackground", "colorValue", - } - for _, prop := range angularProps { - if _, exists := panel[prop]; exists { - return panel - } - } - - return map[string]interface{}{} -} - -// getReducerForValueName returns the mapped reducer or empty string for invalid values -func getReducerForValueName(valueName string) string { - reducerMap := map[string]string{ - "min": "min", - "max": "max", - "mean": "mean", - "avg": "mean", // avg maps to mean - "median": "median", - "sum": "sum", - "count": "count", - "first": "firstNotNull", - "last": "lastNotNull", - "name": "lastNotNull", - "current": "lastNotNull", - "total": "sum", - } - - if reducer, ok := reducerMap[valueName]; ok { - return reducer - } - - return "" -} - -func migrateThresholdsAndColors(defaults map[string]interface{}, thresholdsStr string, colors []interface{}) { - // Parse thresholds string (e.g., "10,20,30") - // Based on sharedSingleStatPanelChangedHandler lines ~145-165: Convert thresholds and color values - thresholds := []interface{}{} - thresholdValues := strings.Split(thresholdsStr, ",") - - // Create threshold steps - for i, color := range colors { - step := map[string]interface{}{ - "color": color, - } - - if i == 0 { - // Frontend expects explicit null value for first step, not omitted field - // Use a pointer to ensure the field is present in JSON with null value - var nullValue *float64 - step["value"] = nullValue - } else if i-1 < len(thresholdValues) { - if val, err := strconv.ParseFloat(strings.TrimSpace(thresholdValues[i-1]), 64); err == nil { - step["value"] = val - } - } - - thresholds = append(thresholds, step) - } - - defaults["thresholds"] = map[string]interface{}{ - "mode": "absolute", - "steps": thresholds, - } -} - -func migrateValueMappings(panel map[string]interface{}, defaults map[string]interface{}, valueMappings []interface{}) { - mappings := []interface{}{} - mappingType := panel["mappingType"] - - // Check for inconsistent mapping configuration - // If panel has rangeMaps but mappingType is 1, or vice versa, fix it - hasValueMaps := panel["valueMaps"] != nil && IsArray(panel["valueMaps"]) && len(panel["valueMaps"].([]interface{})) > 0 - hasRangeMaps := panel["rangeMaps"] != nil && IsArray(panel["rangeMaps"]) && len(panel["rangeMaps"].([]interface{})) > 0 - - if hasRangeMaps && mappingType == float64(1) { - mappingType = 2 - } else if hasValueMaps && mappingType == float64(2) { - mappingType = 1 - } else if mappingType == nil { - if hasValueMaps { - mappingType = 1 - } else if hasRangeMaps { - mappingType = 2 - } - } - - switch mappingType { - case 1: - for _, valueMap := range valueMappings { - valueMapping := valueMap.(map[string]interface{}) - upgradedMapping := upgradeOldAngularValueMapping(valueMapping, defaults["thresholds"]) - if upgradedMapping != nil { - mappings = append(mappings, upgradedMapping) - } - } - case 2: - // Handle range mappings - if rangeMaps, ok := panel["rangeMaps"].([]interface{}); ok { - for _, rangeMap := range rangeMaps { - rangeMapping := rangeMap.(map[string]interface{}) - upgradedMapping := upgradeOldAngularValueMapping(rangeMapping, defaults["thresholds"]) - if upgradedMapping != nil { - mappings = append(mappings, upgradedMapping) - } - } - } - } - - defaults["mappings"] = mappings -} - -// upgradeOldAngularValueMapping converts old angular value mappings to new format -// Based on upgradeOldAngularValueMapping in packages/grafana-data/src/utils/valueMappings.ts -func upgradeOldAngularValueMapping(old map[string]interface{}, thresholds interface{}) map[string]interface{} { - valueMaps := map[string]interface{}{ - "type": "value", - "options": map[string]interface{}{}, - } - newMappings := []interface{}{} - - // Use the color we would have picked from thresholds - // Frontend uses old.text to determine color, not old.value - var color interface{} - if text, ok := old["text"].(string); ok { - if numeric, err := parseNumericValue(text); err == nil { - if thresholdsMap, ok := thresholds.(map[string]interface{}); ok { - if steps, ok := thresholdsMap["steps"].([]interface{}); ok { - level := getActiveThreshold(numeric, steps) - if level != nil { - if levelColor, ok := level["color"]; ok { - color = levelColor - } - } - } - } - } - } - - // Determine mapping type - mappingType := old["type"] - if mappingType == nil { - // Try to guess from available properties - if old["value"] != nil { - mappingType = 1 // ValueToText - } else if old["from"] != nil || old["to"] != nil { - mappingType = 2 // RangeToText - } - } - - switch mappingType { - case 1: // ValueToText - if value, ok := old["value"]; ok && value != nil { - if valueStr, ok := value.(string); ok && valueStr == "null" { - newMappings = append(newMappings, map[string]interface{}{ - "type": "special", - "options": map[string]interface{}{ - "match": "null", - "result": map[string]interface{}{"text": old["text"], "color": color}, - }, - }) - } else { - valueMaps["options"].(map[string]interface{})[fmt.Sprintf("%v", value)] = map[string]interface{}{ - "text": old["text"], - "color": color, - } - } - } - case 2: // RangeToText - from := old["from"] - to := old["to"] - if (from != nil && fmt.Sprintf("%v", from) == "null") || (to != nil && fmt.Sprintf("%v", to) == "null") { - newMappings = append(newMappings, map[string]interface{}{ - "type": "special", - "options": map[string]interface{}{ - "match": "null", - "result": map[string]interface{}{"text": old["text"], "color": color}, - }, - }) - } else { - var fromVal, toVal interface{} - if from != nil { - if fromStr, ok := from.(string); ok { - if fromFloat, err := strconv.ParseFloat(fromStr, 64); err == nil { - fromVal = fromFloat - } - } else { - fromVal = from - } - } - if to != nil { - if toStr, ok := to.(string); ok { - if toFloat, err := strconv.ParseFloat(toStr, 64); err == nil { - toVal = toFloat - } - } else { - toVal = to - } - } - - newMappings = append(newMappings, map[string]interface{}{ - "type": "range", - "options": map[string]interface{}{ - "from": fromVal, - "to": toVal, - "result": map[string]interface{}{"text": old["text"], "color": color}, - }, - }) - } - } - - // Add valueMaps if it has options - if len(valueMaps["options"].(map[string]interface{})) > 0 { - newMappings = append([]interface{}{valueMaps}, newMappings...) - } - - if len(newMappings) > 0 { - return newMappings[0].(map[string]interface{}) - } - - return nil -} - -// getActiveThreshold finds the active threshold for a given value -// Based on getActiveThreshold in packages/grafana-data/src/field/thresholds.ts -func getActiveThreshold(value float64, steps []interface{}) map[string]interface{} { - for i := len(steps) - 1; i >= 0; i-- { - if step, ok := steps[i].(map[string]interface{}); ok { - if stepValue, ok := step["value"]; ok { - if stepValue == nil { - // First step with null value (represents -Infinity) - return step - } - if stepFloat, ok := stepValue.(float64); ok && value >= stepFloat { - return step - } - } - } - } - return nil -} - -// parseNumericValue converts various types to float64 for threshold calculations -func parseNumericValue(value interface{}) (float64, error) { - switch v := value.(type) { - case string: - return strconv.ParseFloat(v, 64) - case float64: - return v, nil - case float32: - return float64(v), nil - case int: - return float64(v), nil - case int32: - return float64(v), nil - case int64: - return float64(v), nil - default: - return 0, fmt.Errorf("cannot convert %T to numeric value", value) - } -} - -// createFieldConfigFromDefaults creates the proper fieldConfig structure from defaults -// and removes all legacy properties from the panel -func createFieldConfigFromDefaults(panel map[string]interface{}, defaults map[string]interface{}) { - // Ensure fieldConfig exists - if panel["fieldConfig"] == nil { - panel["fieldConfig"] = map[string]interface{}{ - "defaults": map[string]interface{}{}, - "overrides": []interface{}{}, - } - } - - fieldConfig := panel["fieldConfig"].(map[string]interface{}) - fieldDefaults := fieldConfig["defaults"].(map[string]interface{}) - - // Copy all defaults to fieldConfig.defaults - for key, value := range defaults { - fieldDefaults[key] = value - } - - // Note: Frontend doesn't add these extra fieldConfig defaults - // Color is handled in sparkline migration logic - // nullValueMode and unit are not added by frontend - - // Remove all legacy properties from the panel - legacyProperties := []string{ - "colors", "thresholds", "valueMaps", "grid", "legend", "mappingTypes", "gauge", - "autoMigrateFrom", "colorBackground", "colorValue", "format", "mappingType", - "nullPointMode", "postfix", "postfixFontSize", "prefix", - "prefixFontSize", "rangeMaps", "sparkline", "tableColumn", "valueFontSize", - "valueName", "aliasYAxis", "bars", "dashLength", "dashes", "fill", "fillGradient", - "lineInterpolation", "lineWidth", "pointRadius", "points", "spaceLength", - "stack", "steppedLine", "xAxis", "yAxes", "yAxis", "zIndex", - } - - for _, prop := range legacyProperties { - delete(panel, prop) - } -} - -// cleanupAngularProperties removes old angular properties after migration -// Based on PanelModel.clearPropertiesBeforePluginChange in public/app/features/dashboard/state/PanelModel.ts -// This function removes ALL properties except those in mustKeepProps to match frontend behavior exactly -func cleanupAngularProperties(panel map[string]interface{}) { - // Properties that must be kept (matching frontend mustKeepProps) - mustKeepProps := map[string]bool{ - "id": true, "gridPos": true, "type": true, "title": true, "scopedVars": true, - "repeat": true, "repeatPanelId": true, "repeatDirection": true, "repeatedByRow": true, - "minSpan": true, "collapsed": true, "panels": true, "targets": true, "datasource": true, - "timeFrom": true, "timeShift": true, "hideTimeOverride": true, "description": true, - "links": true, "fullscreen": true, "isEditing": true, "isViewing": true, - "hasRefreshed": true, "events": true, "cacheTimeout": true, "queryCachingTTL": true, - "cachedPluginOptions": true, "transparent": true, "pluginVersion": true, - "fieldConfig": true, "options": true, // These are set by migration - "maxDataPoints": true, "interval": true, // Panel-level properties preserved by frontend - "autoMigrateFrom": true, // Preserve autoMigrateFrom for proper migration logic - } - - // Remove ALL properties except those in mustKeepProps (matching frontend behavior) - for key := range panel { - if !mustKeepProps[key] { - delete(panel, key) - } - } - - // Ensure all targets have refIds (matching frontend ensureQueryIds behavior) - ensureTargetRefIds(panel) -} - -// ensureTargetRefIds assigns refIds to targets that don't have them -// This matches the frontend PanelModel.ensureQueryIds() behavior -func ensureTargetRefIds(panel map[string]interface{}) { - targets, ok := panel["targets"].([]interface{}) - if !ok || len(targets) == 0 { - return - } - - // Find existing refIds - existingRefIds := make(map[string]bool) - for _, targetInterface := range targets { - if target, ok := targetInterface.(map[string]interface{}); ok { - if refId, ok := target["refId"].(string); ok { - existingRefIds[refId] = true - } - } - } - - // Assign refIds to targets that don't have them - letters := "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - letterIndex := 0 - - for _, targetInterface := range targets { - if target, ok := targetInterface.(map[string]interface{}); ok { - refId, hasRefId := target["refId"].(string) - if !hasRefId || refId == "" { - // Find next available refId - for letterIndex < len(letters) { - refId := string(letters[letterIndex]) - if !existingRefIds[refId] { - target["refId"] = refId - existingRefIds[refId] = true - break - } - letterIndex++ - } - letterIndex++ - } - } - } -} - // removeDeprecatedVariableProperties removes deprecated properties from variables // Based on DashboardMigrator.ts v28 migration: variable property cleanup func removeDeprecatedVariableProperties(variable map[string]interface{}) { @@ -843,45 +69,3 @@ func removeDeprecatedVariableProperties(variable map[string]interface{}) { } } } - -// determineColorMode determines the color mode based on angular options -func determineColorMode(angularOpts map[string]interface{}) string { - if colorBackground, ok := angularOpts["colorBackground"].(bool); ok && colorBackground { - return "background" - } - - if colorValue, ok := angularOpts["colorValue"].(bool); ok && colorValue { - return "value" - } - - return "none" -} - -// migrateSparklineColor migrates sparkline color configuration when colorMode is "none" -// Based on statPanelChangedHandler lines 31-38 -func migrateSparklineColor(angularOpts map[string]interface{}, defaults map[string]interface{}, options map[string]interface{}) { - sparkline, ok := angularOpts["sparkline"].(map[string]interface{}) - if !ok { - return - } - - show, ok := sparkline["show"].(bool) - if !ok || !show { - return - } - - graphMode, ok := options["graphMode"].(string) - if !ok || graphMode != "area" { - return - } - - lineColor, ok := sparkline["lineColor"].(string) - if !ok { - return - } - - defaults["color"] = map[string]interface{}{ - "mode": "fixed", - "fixedColor": lineColor, - } -} diff --git a/apps/dashboard/pkg/migration/schemaversion/v28_test.go b/apps/dashboard/pkg/migration/schemaversion/v28_test.go index 68fc1c5fedd..b597d913f98 100644 --- a/apps/dashboard/pkg/migration/schemaversion/v28_test.go +++ b/apps/dashboard/pkg/migration/schemaversion/v28_test.go @@ -5,101 +5,186 @@ import ( "testing" ) -func TestV28SinglestatMigration(t *testing.T) { - tests := []struct { - name string - input map[string]interface{} - expected map[string]interface{} - description string - }{ - { - name: "migrate_range_maps_to_field_config_mappings", - input: map[string]interface{}{ - "type": "singlestat", - "rangeMaps": []interface{}{ - map[string]interface{}{ - "from": "null", - "to": "N/A", - }, - }, - "mappingType": 1, // Inconsistent - should be 2 for rangeMaps - }, - expected: map[string]interface{}{ - "type": "stat", - "fieldConfig": map[string]interface{}{ - "defaults": map[string]interface{}{ - "mappings": []interface{}{ - map[string]interface{}{ - "options": map[string]interface{}{ - "match": "null", - "result": map[string]interface{}{ - "text": "N/A", - }, - }, - "type": "special", - }, - }, - }, - }, - }, - description: "RangeMaps should migrate to fieldConfig.mappings, and inconsistent mappingType should be fixed to 2 (RangeToText)", - }, - { - name: "migrate_sparkline_color_when_color_mode_none", - input: map[string]interface{}{ - "type": "singlestat", - "colorMode": "None", - "sparkline": map[string]interface{}{ - "lineColor": "rgb(31, 120, 193)", - }, - }, - expected: map[string]interface{}{ - "type": "stat", - "fieldConfig": map[string]interface{}{ - "defaults": map[string]interface{}{ - "color": map[string]interface{}{ - "mode": "fixed", - "fixedColor": "rgb(31, 120, 193)", - }, - }, - }, - }, - description: "Sparkline lineColor should migrate to fieldConfig.defaults.color only when colorMode is None", - }, - } +type migrationTestCase struct { + name string + input map[string]interface{} + expected map[string]interface{} +} +func runMigrationTests(t *testing.T, tests []migrationTestCase, migrationFunc func(context.Context, map[string]interface{}) error) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - dashboard := map[string]interface{}{ - "schemaVersion": 27, - "panels": []interface{}{tt.input}, + // Create a copy of the input + dashboard := make(map[string]interface{}) + for k, v := range tt.input { + dashboard[k] = v } - err := V28(context.Background(), dashboard) + err := migrationFunc(context.Background(), dashboard) if err != nil { - t.Fatalf("V28 migration failed: %v", err) + t.Fatalf("Migration failed: %v", err) } - if dashboard["schemaVersion"] != 28 { - t.Errorf("Expected schemaVersion to be 28, got %v", dashboard["schemaVersion"]) + // Verify the result matches expected + if !deepEqual(dashboard, tt.expected) { + t.Errorf("Migration result doesn't match expected.\nExpected: %+v\nGot: %+v", tt.expected, dashboard) } - - panels, ok := dashboard["panels"].([]interface{}) - if !ok || len(panels) == 0 { - t.Fatalf("Expected panels array with at least one panel") - } - - panel, ok := panels[0].(map[string]interface{}) - if !ok { - t.Fatalf("Expected panel to be a map") - } - - // Verify panel type was changed to stat - if panel["type"] != "stat" { - t.Errorf("Expected panel type to be 'stat', got %v", panel["type"]) - } - - t.Logf("✓ %s: %s", tt.name, tt.description) }) } } + +func deepEqual(a, b interface{}) bool { + // Simple deep comparison for test purposes + // This is a simplified version - in production you'd use reflect.DeepEqual or similar + switch aVal := a.(type) { + case map[string]interface{}: + bVal, ok := b.(map[string]interface{}) + if !ok || len(aVal) != len(bVal) { + return false + } + for k, v := range aVal { + if !deepEqual(v, bVal[k]) { + return false + } + } + return true + case []interface{}: + bVal, ok := b.([]interface{}) + if !ok || len(aVal) != len(bVal) { + return false + } + for i, v := range aVal { + if !deepEqual(v, bVal[i]) { + return false + } + } + return true + default: + return a == b + } +} + +func TestV28(t *testing.T) { + tests := []migrationTestCase{ + { + name: "v28 removes deprecated variable properties", + input: map[string]interface{}{ + "title": "V28 Variable Properties Migration Test Dashboard", + "schemaVersion": 27, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "name": "var1", + "tags": []interface{}{"tag1", "tag2"}, + "tagsQuery": "query_string", + "tagValuesQuery": "values_query", + "useTags": true, + "type": "query", + }, + map[string]interface{}{ + "name": "var2", + "tags": []interface{}{}, + "tagsQuery": "", // Empty string should not be removed + "tagValuesQuery": "", // Empty string should not be removed + "useTags": false, // False should not be removed + "type": "custom", + }, + }, + }, + "panels": []interface{}{ + map[string]interface{}{ + "type": "singlestat", + "title": "Singlestat Panel (unchanged by v28)", + "id": 1, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V28 Variable Properties Migration Test Dashboard", + "schemaVersion": 28, + "templating": map[string]interface{}{ + "list": []interface{}{ + map[string]interface{}{ + "name": "var1", + "type": "query", + // tags, tagsQuery, tagValuesQuery, useTags should be removed + }, + map[string]interface{}{ + "name": "var2", + "tagsQuery": "", // Empty string preserved + "tagValuesQuery": "", // Empty string preserved + "useTags": false, // False preserved + "type": "custom", + // only tags should be removed + }, + }, + }, + "panels": []interface{}{ + map[string]interface{}{ + "type": "singlestat", + "title": "Singlestat Panel (unchanged by v28)", + "id": 1, + }, + }, + }, + }, + { + name: "v28 handles dashboard without templating", + input: map[string]interface{}{ + "title": "Dashboard without templating", + "schemaVersion": 27, + "panels": []interface{}{ + map[string]interface{}{ + "type": "singlestat", + "title": "Singlestat Panel", + "id": 1, + }, + }, + }, + expected: map[string]interface{}{ + "title": "Dashboard without templating", + "schemaVersion": 28, + "panels": []interface{}{ + map[string]interface{}{ + "type": "singlestat", + "title": "Singlestat Panel", + "id": 1, + }, + }, + }, + }, + { + name: "v28 handles empty templating list", + input: map[string]interface{}{ + "title": "Dashboard with empty templating", + "schemaVersion": 27, + "templating": map[string]interface{}{ + "list": []interface{}{}, + }, + "panels": []interface{}{ + map[string]interface{}{ + "type": "singlestat", + "title": "Singlestat Panel", + "id": 1, + }, + }, + }, + expected: map[string]interface{}{ + "title": "Dashboard with empty templating", + "schemaVersion": 28, + "templating": map[string]interface{}{ + "list": []interface{}{}, + }, + "panels": []interface{}{ + map[string]interface{}{ + "type": "singlestat", + "title": "Singlestat Panel", + "id": 1, + }, + }, + }, + }, + } + + runMigrationTests(t, tests, V28) +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v36.go b/apps/dashboard/pkg/migration/schemaversion/v36.go index c45c4ad56a2..7874e80b1d9 100644 --- a/apps/dashboard/pkg/migration/schemaversion/v36.go +++ b/apps/dashboard/pkg/migration/schemaversion/v36.go @@ -139,7 +139,8 @@ func migrateTemplateVariables(dashboard map[string]interface{}, datasources []Da ds, exists := varMap["datasource"] // Handle null datasource variables by setting to default (matches frontend behavior) - if !exists || ds == nil { + // Only add datasource if it's explicitly null, not if it doesn't exist + if exists && ds == nil { varMap["datasource"] = GetDataSourceRef(defaultDS) } // Note: Frontend v36 migration only converts null datasources to default objects diff --git a/apps/dashboard/pkg/migration/schemaversion/v7.go b/apps/dashboard/pkg/migration/schemaversion/v7.go new file mode 100644 index 00000000000..15416f9b608 --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v7.go @@ -0,0 +1,73 @@ +package schemaversion + +import "context" + +// V7 migration handles the nav to timepicker conversion and ensures query refIds. +// This migration transforms the legacy nav property to the newer timepicker format +// and ensures all panel targets have refId properties. +// +// Background: +// In earlier versions, dashboards used a "nav" property array to store time picker +// configuration. This migration moves the first nav item to the "timepicker" property. +// Additionally, it ensures all query targets have refId properties assigned. +// +// Example before migration: +// { +// "schemaVersion": 6, +// "nav": [ +// { +// "enable": true, +// "type": "timepicker", +// "status": "Stable", +// "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"], +// "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], +// "now": true, +// "collapse": false, +// "notice": false +// } +// ], +// "panels": [ +// { +// "targets": [ +// {"expr": "up"}, +// {"expr": "cpu_usage", "refId": "B"} +// ] +// } +// ] +// } +// +// Example after migration: +// { +// "schemaVersion": 7, +// "timepicker": { +// "enable": true, +// "type": "timepicker", +// "status": "Stable", +// "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"], +// "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], +// "now": true, +// "collapse": false, +// "notice": false +// }, +// "panels": [ +// { +// "targets": [ +// {"expr": "up", "refId": "A"}, +// {"expr": "cpu_usage", "refId": "B"} +// ] +// } +// ] +// } + +func V7(_ context.Context, dashboard map[string]interface{}) error { + dashboard["schemaVersion"] = 7 + + // Convert nav to timepicker (matches frontend DashboardMigrator logic) + if nav, ok := dashboard["nav"].([]interface{}); ok && len(nav) > 0 { + if firstNav, ok := nav[0].(map[string]interface{}); ok { + dashboard["timepicker"] = firstNav + } + } + + return nil +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v7_test.go b/apps/dashboard/pkg/migration/schemaversion/v7_test.go new file mode 100644 index 00000000000..5ac73cdf61a --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v7_test.go @@ -0,0 +1,208 @@ +package schemaversion_test + +import ( + "testing" + + "github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" +) + +func TestV7Migration(t *testing.T) { + testCases := []migrationTestCase{ + { + name: "nav to timepicker conversion with query refId assignment", + input: map[string]interface{}{ + "schemaVersion": 6, + "nav": []interface{}{ + map[string]interface{}{ + "enable": true, + "type": "timepicker", + "status": "Stable", + "time_options": []interface{}{"5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"}, + "refresh_intervals": []interface{}{"5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"}, + "now": true, + "collapse": false, + "notice": false, + }, + }, + "panels": []interface{}{ + map[string]interface{}{ + "targets": []interface{}{ + map[string]interface{}{"expr": "up"}, + map[string]interface{}{"expr": "cpu_usage", "refId": "B"}, + }, + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 7, + "nav": []interface{}{ + map[string]interface{}{ + "enable": true, + "type": "timepicker", + "status": "Stable", + "time_options": []interface{}{"5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"}, + "refresh_intervals": []interface{}{"5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"}, + "now": true, + "collapse": false, + "notice": false, + }, + }, + "timepicker": map[string]interface{}{ + "enable": true, + "type": "timepicker", + "status": "Stable", + "time_options": []interface{}{"5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"}, + "refresh_intervals": []interface{}{"5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"}, + "now": true, + "collapse": false, + "notice": false, + }, + "panels": []interface{}{ + map[string]interface{}{ + "targets": []interface{}{ + map[string]interface{}{"expr": "up"}, + map[string]interface{}{"expr": "cpu_usage", "refId": "B"}, + }, + }, + }, + }, + }, + { + name: "nav conversion without panels", + input: map[string]interface{}{ + "schemaVersion": 6, + "nav": []interface{}{ + map[string]interface{}{ + "enable": true, + "type": "timepicker", + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 7, + "timepicker": map[string]interface{}{ + "enable": true, + "type": "timepicker", + }, + "nav": []interface{}{ + map[string]interface{}{ + "enable": true, + "type": "timepicker", + }, + }, + }, + }, + { + name: "empty nav array", + input: map[string]interface{}{ + "schemaVersion": 6, + "nav": []interface{}{}, + }, + expected: map[string]interface{}{ + "schemaVersion": 7, + "nav": []interface{}{}, + }, + }, + { + name: "no nav property", + input: map[string]interface{}{ + "schemaVersion": 6, + "title": "Test Dashboard", + }, + expected: map[string]interface{}{ + "schemaVersion": 7, + "title": "Test Dashboard", + }, + }, + { + name: "panels with nested panels", + input: map[string]interface{}{ + "schemaVersion": 6, + "panels": []interface{}{ + map[string]interface{}{ + "type": "row", + "panels": []interface{}{ + map[string]interface{}{ + "targets": []interface{}{ + map[string]interface{}{"expr": "memory_usage"}, + }, + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 7, + "panels": []interface{}{ + map[string]interface{}{ + "type": "row", + "panels": []interface{}{ + map[string]interface{}{ + "targets": []interface{}{ + map[string]interface{}{"expr": "memory_usage"}, + }, + }, + }, + }, + }, + }, + }, + { + name: "multiple nav items - only first is used", + input: map[string]interface{}{ + "schemaVersion": 6, + "nav": []interface{}{ + map[string]interface{}{ + "enable": true, + "type": "timepicker", + }, + map[string]interface{}{ + "enable": false, + "type": "other", + }, + }, + }, + expected: map[string]interface{}{ + "schemaVersion": 7, + "timepicker": map[string]interface{}{ + "enable": true, + "type": "timepicker", + }, + "nav": []interface{}{ + map[string]interface{}{ + "enable": true, + "type": "timepicker", + }, + map[string]interface{}{ + "enable": false, + "type": "other", + }, + }, + }, + }, + { + name: "invalid nav structure", + input: map[string]interface{}{ + "schemaVersion": 6, + "nav": "invalid", + }, + expected: map[string]interface{}{ + "schemaVersion": 7, + "nav": "invalid", + }, + }, + { + name: "panels with invalid structure", + input: map[string]interface{}{ + "schemaVersion": 6, + "panels": "invalid", + }, + expected: map[string]interface{}{ + "schemaVersion": 7, + "panels": "invalid", + }, + }, + } + + runMigrationTests(t, testCases, schemaversion.V7) +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v8.go b/apps/dashboard/pkg/migration/schemaversion/v8.go new file mode 100644 index 00000000000..ec8c3940caf --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v8.go @@ -0,0 +1,206 @@ +package schemaversion + +import "context" + +// V8 migration updates old InfluxDB query schema to the new format. +// This migration transforms the legacy InfluxDB query structure with fields, tags, and groupBy +// into the newer select-based query format. +// +// Background: +// In earlier versions, InfluxDB queries were stored using a different schema with separate +// fields, tags, and groupBy properties. This migration converts them to the newer select +// format that is more structured and easier to work with. +// +// The migration handles two cases: +// 1. Raw queries: Simply removes the fields and fill properties +// 2. Structured queries: Converts fields to select format and updates groupBy structure +// +// Example before migration (structured query): +// { +// "schemaVersion": 7, +// "panels": [ +// { +// "targets": [ +// { +// "fields": [ +// {"name": "value", "func": "mean", "mathExpr": "*2", "asExpr": "doubled"} +// ], +// "tags": [{"key": "host", "value": "server1"}], +// "groupBy": [ +// {"type": "time", "interval": "1m"}, +// {"type": "tag", "key": "host"} +// ], +// "fill": "null" +// } +// ] +// } +// ] +// } +// +// Example after migration (structured query): +// { +// "schemaVersion": 8, +// "panels": [ +// { +// "targets": [ +// { +// "select": [ +// [ +// {"type": "field", "params": ["value"]}, +// {"type": "mean", "params": []}, +// {"type": "math", "params": ["*2"]}, +// {"type": "alias", "params": ["doubled"]} +// ] +// ], +// "tags": [{"key": "host", "value": "server1"}], +// "groupBy": [ +// {"type": "time", "params": ["1m"]}, +// {"type": "tag", "params": ["host"]}, +// {"type": "fill", "params": ["null"]} +// ] +// } +// ] +// } +// ] +// } + +func V8(_ context.Context, dashboard map[string]interface{}) error { + dashboard["schemaVersion"] = 8 + + panels, ok := dashboard["panels"].([]interface{}) + if !ok { + return nil + } + + for _, p := range panels { + panel, ok := p.(map[string]interface{}) + if !ok { + continue + } + + targets, ok := panel["targets"].([]interface{}) + if !ok { + continue + } + + for _, t := range targets { + target, ok := t.(map[string]interface{}) + if !ok { + continue + } + + // Check if this target has the old InfluxDB schema + fields, hasFields := target["fields"] + _, hasTags := target["tags"] + groupBy, hasGroupBy := target["groupBy"] + + if !hasFields || !hasTags || !hasGroupBy { + continue + } + + // Check if this is a raw query + rawQuery, isRawQuery := target["rawQuery"].(bool) + if isRawQuery && rawQuery { + // For raw queries, just delete fields and fill + delete(target, "fields") + delete(target, "fill") + } else { + // For structured queries, convert fields to select format + fieldsArray, ok := fields.([]interface{}) + if ok { + selectArray := make([]interface{}, 0, len(fieldsArray)) + + for _, f := range fieldsArray { + field, ok := f.(map[string]interface{}) + if !ok { + continue + } + + parts := make([]interface{}, 0) + + // Add field part + if name, ok := field["name"].(string); ok { + parts = append(parts, map[string]interface{}{ + "type": "field", + "params": []interface{}{name}, + }) + } + + // Add function part + if funcName, ok := field["func"].(string); ok { + parts = append(parts, map[string]interface{}{ + "type": funcName, + "params": []interface{}{}, + }) + } + + // Add math expression if present + if mathExpr, ok := field["mathExpr"].(string); ok { + parts = append(parts, map[string]interface{}{ + "type": "math", + "params": []interface{}{mathExpr}, + }) + } + + // Add alias if present + if asExpr, ok := field["asExpr"].(string); ok { + parts = append(parts, map[string]interface{}{ + "type": "alias", + "params": []interface{}{asExpr}, + }) + } + + if len(parts) > 0 { + selectArray = append(selectArray, parts) + } + } + + target["select"] = selectArray + } + + // Remove the old fields property + delete(target, "fields") + + // Update groupBy format + if groupByArray, ok := groupBy.([]interface{}); ok { + for _, gb := range groupByArray { + groupByPart, ok := gb.(map[string]interface{}) + if !ok { + continue + } + + // Convert time groupBy + if partType, ok := groupByPart["type"].(string); ok && partType == "time" { + if interval, ok := groupByPart["interval"].(string); ok { + groupByPart["params"] = []interface{}{interval} + delete(groupByPart, "interval") + } + } + + // Convert tag groupBy + if partType, ok := groupByPart["type"].(string); ok && partType == "tag" { + if key, ok := groupByPart["key"].(string); ok { + groupByPart["params"] = []interface{}{key} + delete(groupByPart, "key") + } + } + } + + // Add fill to groupBy if present + if fill, hasFill := target["fill"]; hasFill { + newGroupByArray := make([]interface{}, len(groupByArray)) + copy(newGroupByArray, groupByArray) + newGroupByArray = append(newGroupByArray, map[string]interface{}{ + "type": "fill", + "params": []interface{}{fill}, + }) + target["groupBy"] = newGroupByArray + delete(target, "fill") + } + } + } + } + } + + return nil +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v8_test.go b/apps/dashboard/pkg/migration/schemaversion/v8_test.go new file mode 100644 index 00000000000..725e93c6334 --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v8_test.go @@ -0,0 +1,252 @@ +package schemaversion_test + +import ( + "testing" + + "github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" +) + +func TestV8(t *testing.T) { + tests := []migrationTestCase{ + { + name: "InfluxDB structured query should be converted to new select format", + input: map[string]interface{}{ + "title": "V8 InfluxDB Query Migration Test Dashboard", + "schemaVersion": 7, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "targets": []interface{}{ + map[string]interface{}{ + "fields": []interface{}{ + map[string]interface{}{ + "name": "value", + "func": "mean", + "mathExpr": "*2", + "asExpr": "doubled", + }, + map[string]interface{}{ + "name": "count", + "func": "sum", + }, + }, + "tags": []interface{}{ + map[string]interface{}{ + "key": "host", + "value": "server1", + }, + }, + "groupBy": []interface{}{ + map[string]interface{}{ + "type": "time", + "interval": "1m", + }, + map[string]interface{}{ + "type": "tag", + "key": "host", + }, + }, + "fill": "null", + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V8 InfluxDB Query Migration Test Dashboard", + "schemaVersion": 8, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "targets": []interface{}{ + map[string]interface{}{ + "select": []interface{}{ + []interface{}{ + map[string]interface{}{ + "type": "field", + "params": []interface{}{"value"}, + }, + map[string]interface{}{ + "type": "mean", + "params": []interface{}{}, + }, + map[string]interface{}{ + "type": "math", + "params": []interface{}{"*2"}, + }, + map[string]interface{}{ + "type": "alias", + "params": []interface{}{"doubled"}, + }, + }, + []interface{}{ + map[string]interface{}{ + "type": "field", + "params": []interface{}{"count"}, + }, + map[string]interface{}{ + "type": "sum", + "params": []interface{}{}, + }, + }, + }, + "tags": []interface{}{ + map[string]interface{}{ + "key": "host", + "value": "server1", + }, + }, + "groupBy": []interface{}{ + map[string]interface{}{ + "type": "time", + "params": []interface{}{"1m"}, + }, + map[string]interface{}{ + "type": "tag", + "params": []interface{}{"host"}, + }, + map[string]interface{}{ + "type": "fill", + "params": []interface{}{"null"}, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "InfluxDB raw query should only remove fields and fill", + input: map[string]interface{}{ + "title": "V8 InfluxDB Raw Query Migration Test Dashboard", + "schemaVersion": 7, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "targets": []interface{}{ + map[string]interface{}{ + "rawQuery": true, + "fields": []interface{}{ + map[string]interface{}{ + "name": "value", + "func": "mean", + }, + }, + "tags": []interface{}{ + map[string]interface{}{ + "key": "host", + "value": "server1", + }, + }, + "groupBy": []interface{}{ + map[string]interface{}{ + "type": "time", + "interval": "1m", + }, + }, + "fill": "null", + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V8 InfluxDB Raw Query Migration Test Dashboard", + "schemaVersion": 8, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "targets": []interface{}{ + map[string]interface{}{ + "rawQuery": true, + "tags": []interface{}{ + map[string]interface{}{ + "key": "host", + "value": "server1", + }, + }, + "groupBy": []interface{}{ + map[string]interface{}{ + "type": "time", + "interval": "1m", + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "targets without old InfluxDB schema should remain unchanged", + input: map[string]interface{}{ + "title": "V8 Non-InfluxDB Target Test Dashboard", + "schemaVersion": 7, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "targets": []interface{}{ + map[string]interface{}{ + "expr": "up", + "refId": "A", + "format": "time_series", + }, + }, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V8 Non-InfluxDB Target Test Dashboard", + "schemaVersion": 8, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "targets": []interface{}{ + map[string]interface{}{ + "expr": "up", + "refId": "A", + "format": "time_series", + }, + }, + }, + }, + }, + }, + { + name: "panels without targets should remain unchanged", + input: map[string]interface{}{ + "title": "V8 No Targets Test Dashboard", + "schemaVersion": 7, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "text", + }, + }, + }, + expected: map[string]interface{}{ + "title": "V8 No Targets Test Dashboard", + "schemaVersion": 8, + "panels": []interface{}{ + map[string]interface{}{ + "id": 1, + "type": "text", + }, + }, + }, + }, + { + name: "dashboard without panels should only update schema version", + input: map[string]interface{}{ + "title": "V8 No Panels Test Dashboard", + "schemaVersion": 7, + }, + expected: map[string]interface{}{ + "title": "V8 No Panels Test Dashboard", + "schemaVersion": 8, + }, + }, + } + runMigrationTests(t, tests, schemaversion.V8) +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v9.go b/apps/dashboard/pkg/migration/schemaversion/v9.go new file mode 100644 index 00000000000..dde0fcc3cea --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v9.go @@ -0,0 +1,42 @@ +package schemaversion + +import ( + "context" +) + +// V9 migration removes the first threshold value from singlestat panel thresholds when they have 3 or more values. +// This migration aligns with the frontend schema version 9 changes that addressed aliasYAxis changes +// specifically for singlestat panels with threshold configurations. +// +// Background: +// In earlier versions, singlestat panels stored threshold values as comma-separated strings with the first element +// representing a baseline value that was not actually used in threshold calculations. This migration removes that +// unused first element to clean up the data structure. +// +// Example before migration: +// { +// "schemaVersion": 8, +// "panels": [ +// { +// "type": "singlestat", +// "thresholds": "10,20,30" +// } +// ] +// } +// +// Example after migration: +// { +// "schemaVersion": 9, +// "panels": [ +// { +// "type": "singlestat", +// "thresholds": "20,30" +// } +// ] +// } + +func V9(_ context.Context, dashboard map[string]interface{}) error { + dashboard["schemaVersion"] = 9 + + return nil +} diff --git a/apps/dashboard/pkg/migration/schemaversion/v9_test.go b/apps/dashboard/pkg/migration/schemaversion/v9_test.go new file mode 100644 index 00000000000..aa6bf311711 --- /dev/null +++ b/apps/dashboard/pkg/migration/schemaversion/v9_test.go @@ -0,0 +1,38 @@ +package schemaversion_test + +import ( + "testing" + + "github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion" +) + +func TestV9(t *testing.T) { + tests := []migrationTestCase{ + { + name: "v11 no-op migration, updates schema version only", + input: map[string]interface{}{ + "title": "V9 No-Op Migration Test Dashboard", + "schemaVersion": 8, + "panels": []interface{}{ + map[string]interface{}{ + "type": "graph", + "title": "Panel remains unchanged", + "id": 1, + }, + }, + }, + expected: map[string]interface{}{ + "title": "V9 No-Op Migration Test Dashboard", + "schemaVersion": 9, + "panels": []interface{}{ + map[string]interface{}{ + "type": "graph", + "title": "Panel remains unchanged", + "id": 1, + }, + }, + }, + }, + } + runMigrationTests(t, tests, schemaversion.V9) +} diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mssql/mssql_fakedata.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mssql/mssql_fakedata.v42.json index 9d091b0a35d..45c41bc2847 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mssql/mssql_fakedata.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mssql/mssql_fakedata.v42.json @@ -327,60 +327,12 @@ } }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mssql" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 10, "w": 24, @@ -388,11 +340,35 @@ "y": 18 }, "id": 4, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -405,14 +381,7 @@ } ], "title": "Values", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" } ], diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mssql/mssql_unittest.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mssql/mssql_unittest.v42.json index 9f651c9bf02..36dbc69d1cc 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mssql/mssql_unittest.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mssql/mssql_unittest.v42.json @@ -77,40 +77,12 @@ "links": [], "panels": [ { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mssql-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, + "fontSize": "100%", "gridPos": { "h": 4, "w": 24, @@ -118,11 +90,28 @@ "y": 0 }, "id": 2, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "string", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -135,71 +124,16 @@ } ], "title": "Data types", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mssql-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -207,11 +141,35 @@ "y": 4 }, "id": 32, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -225,71 +183,16 @@ } ], "title": "cast(null as bigint) as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mssql-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -297,11 +200,35 @@ "y": 4 }, "id": 33, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -315,71 +242,16 @@ } ], "title": "cast(null as datetime) as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mssql-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -387,11 +259,35 @@ "y": 4 }, "id": 34, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -405,71 +301,16 @@ } ], "title": "GETDATE() as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mssql-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -477,11 +318,35 @@ "y": 4 }, "id": 35, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -495,14 +360,7 @@ } ], "title": "GETUTCDATE() as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mysql/mysql_fakedata.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mysql/mysql_fakedata.v42.json index e05dc3f21f7..e946b4f7c53 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mysql/mysql_fakedata.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mysql/mysql_fakedata.v42.json @@ -329,60 +329,12 @@ } }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mysql" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 9, "w": 24, @@ -390,11 +342,36 @@ "y": 18 }, "id": 6, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "link": false, + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -408,14 +385,7 @@ } ], "title": "Values", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" } ], diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mysql/mysql_unittest.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mysql/mysql_unittest.v42.json index d528c6d6198..02768fa9b68 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mysql/mysql_unittest.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-mysql/mysql_unittest.v42.json @@ -77,40 +77,12 @@ "links": [], "panels": [ { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mysql-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, + "fontSize": "100%", "gridPos": { "h": 4, "w": 24, @@ -118,11 +90,28 @@ "y": 0 }, "id": 2, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "string", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -135,71 +124,16 @@ } ], "title": "Data types", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mysql-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time_sec" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -207,11 +141,35 @@ "y": 4 }, "id": 32, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time_sec", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -225,71 +183,16 @@ } ], "title": "cast(null as unsigned integer) as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mysql-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time_sec" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -297,11 +200,35 @@ "y": 4 }, "id": 33, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time_sec", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -315,71 +242,16 @@ } ], "title": "cast(null as datetime) as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mysql-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time_sec" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -387,11 +259,35 @@ "y": 4 }, "id": 34, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time_sec", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -405,71 +301,16 @@ } ], "title": "cast()NOW() as datetime) as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-mysql-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -477,11 +318,35 @@ "y": 4 }, "id": 35, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -495,14 +360,7 @@ } ], "title": "NOW() as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-postgres/postgres_fakedata.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-postgres/postgres_fakedata.v42.json index 8d8faf154cc..d30bfa70e7e 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-postgres/postgres_fakedata.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-postgres/postgres_fakedata.v42.json @@ -371,60 +371,12 @@ } }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-postgres" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 9, "w": 24, @@ -432,11 +384,36 @@ "y": 18 }, "id": 6, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "link": false, + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -450,14 +427,7 @@ } ], "title": "Values", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" } ], diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-postgres/postgres_unittest.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-postgres/postgres_unittest.v42.json index a86b8d4c1be..3bdbf26db6a 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-postgres/postgres_unittest.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/datasource-postgres/postgres_unittest.v42.json @@ -77,40 +77,12 @@ "links": [], "panels": [ { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-postgres-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, + "fontSize": "100%", "gridPos": { "h": 4, "w": 24, @@ -118,11 +90,28 @@ "y": 0 }, "id": 2, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 1, + "desc": false }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "string", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -135,71 +124,16 @@ } ], "title": "Data types", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-postgres-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -207,11 +141,35 @@ "y": 4 }, "id": 32, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -225,71 +183,16 @@ } ], "title": "cast(null as bigint) as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-postgres-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -297,11 +200,35 @@ "y": 4 }, "id": 33, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -315,71 +242,16 @@ } ], "title": "cast(null as datetime) as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-postgres-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -387,11 +259,35 @@ "y": 4 }, "id": 34, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -405,71 +301,16 @@ } ], "title": "localtimestamp as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "uid": "gdev-postgres-ds-tests" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 3, "w": 6, @@ -477,11 +318,35 @@ "y": 4 }, "id": 35, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "", @@ -495,14 +360,7 @@ } ], "title": "NOW() as time", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/feature-templating/testdata-nested-variables-drilldown.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/feature-templating/testdata-nested-variables-drilldown.v42.json index d86c55cc494..c1a9689ddd8 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/feature-templating/testdata-nested-variables-drilldown.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/feature-templating/testdata-nested-variables-drilldown.v42.json @@ -49,44 +49,26 @@ "type": "text" }, { + "autoMigrateFrom": "singlestat", + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], "datasource": { "apiVersion": "v1", "type": "grafana-testdata-datasource", "uid": "testdata-type-uid" }, - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "rgb(31, 120, 193)", - "mode": "fixed" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true }, "gridPos": { "h": 6, @@ -102,25 +84,37 @@ "url": "d/-Y-tnEDWk/dashboard-tests-nested-template-variables?orgId=1\u0026${__all_variables}\u0026${__url_time_range}" } ], - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -132,8 +126,18 @@ "scenarioId": "random_walk" } ], + "thresholds": "", "title": "Panel drilldown link test", - "type": "stat" + "type": "stat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { "aliasColors": {}, diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/feature-templating/testdata-nested-variables.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/feature-templating/testdata-nested-variables.v42.json index a7e8cab830f..8fce11b3b1a 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/feature-templating/testdata-nested-variables.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/feature-templating/testdata-nested-variables.v42.json @@ -55,43 +55,31 @@ "type": "text" }, { + "autoMigrateFrom": "singlestat", + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], "datasource": { "type": "grafana-testdata-datasource" }, "fieldConfig": { "defaults": { - "color": { - "fixedColor": "rgb(31, 120, 193)", - "mode": "fixed" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" + "custom": {} }, "overrides": [] }, + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, "gridPos": { "h": 9, "w": 4, @@ -106,25 +94,28 @@ "url": "d/O6GmNPvWk/dashboard-tests-nested-template-variables-drilldown?orgId=1\u0026${__all_variables}\u0026${__url_time_range}" } ], + "mappingType": 1, + "mappingTypes": [], "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + "nullPointMode": "connected", + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -134,8 +125,18 @@ "scenarioId": "random_walk" } ], + "thresholds": "", "title": "Panel drilldown link test", - "type": "stat" + "type": "stat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { "datasource": { diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-common/lazy_loading.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-common/lazy_loading.v42.json index da8363866ae..0079708be32 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-common/lazy_loading.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-common/lazy_loading.v42.json @@ -191,38 +191,24 @@ "type": "bargauge" }, { + "autoMigrateFrom": "singlestat", + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "#73BF69", + "#d44a3a" + ], "datasource": { "type": "grafana-testdata-datasource" }, - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] + "format": "ms", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true }, "gridPos": { "h": 4, @@ -231,25 +217,38 @@ "y": 7 }, "id": 20, - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "pluginVersion": "6.2.0-pre", + "postfix": "", + "postfixFontSize": "50%", + "prefix": "p99", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -259,41 +258,37 @@ "scenarioId": "random_walk" } ], - "type": "stat" + "thresholds": "", + "type": "stat", + "valueFontSize": "120%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { + "autoMigrateFrom": "singlestat", + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "#73BF69", + "#d44a3a" + ], "datasource": { "type": "grafana-testdata-datasource" }, - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] + "format": "ms", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true }, "gridPos": { "h": 4, @@ -302,25 +297,38 @@ "y": 7 }, "id": 23, - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "pluginVersion": "6.2.0-pre", + "postfix": "", + "postfixFontSize": "50%", + "prefix": "p95", + "prefixFontSize": "80%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -330,41 +338,37 @@ "scenarioId": "random_walk" } ], - "type": "stat" + "thresholds": "", + "type": "stat", + "valueFontSize": "120%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { + "autoMigrateFrom": "singlestat", + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "#73BF69", + "#d44a3a" + ], "datasource": { "type": "grafana-testdata-datasource" }, - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] + "format": "ms", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true }, "gridPos": { "h": 4, @@ -373,25 +377,38 @@ "y": 7 }, "id": 24, - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "pluginVersion": "6.2.0-pre", + "postfix": "", + "postfixFontSize": "50%", + "prefix": "p90", + "prefixFontSize": "80%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -401,41 +418,37 @@ "scenarioId": "random_walk" } ], - "type": "stat" + "thresholds": "", + "type": "stat", + "valueFontSize": "120%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { + "autoMigrateFrom": "singlestat", + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "#73BF69", + "#d44a3a" + ], "datasource": { "type": "grafana-testdata-datasource" }, - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] + "format": "ms", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true }, "gridPos": { "h": 4, @@ -444,25 +457,38 @@ "y": 7 }, "id": 45, - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "pluginVersion": "6.2.0-pre", + "postfix": "", + "postfixFontSize": "50%", + "prefix": "p90", + "prefixFontSize": "80%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -472,7 +498,17 @@ "scenarioId": "random_walk" } ], - "type": "stat" + "thresholds": "", + "type": "stat", + "valueFontSize": "120%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { "aliasColors": { diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-common/panels_without_title.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-common/panels_without_title.v42.json index 68e5cbefd7c..d7fa2960e45 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-common/panels_without_title.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-common/panels_without_title.v42.json @@ -20,45 +20,27 @@ "links": [], "panels": [ { + "autoMigrateFrom": "singlestat", + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "#5794F2", + "#d44a3a" + ], "datasource": { "apiVersion": "v1", "type": "grafana-testdata-datasource", "uid": "testdata-type-uid" }, "description": "asdasdas", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "rgb(31, 120, 193)", - "mode": "fixed" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] + "format": "ms", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true }, "gridPos": { "h": 4, @@ -67,25 +49,37 @@ "y": 0 }, "id": 8, - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "100%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -97,50 +91,42 @@ "scenarioId": "random_walk" } ], + "thresholds": "", "timeShift": "2h", "title": "Title", - "type": "stat" + "type": "stat", + "valueFontSize": "120%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { + "autoMigrateFrom": "singlestat", + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "#5794F2", + "#d44a3a" + ], "datasource": { "apiVersion": "v1", "type": "grafana-testdata-datasource", "uid": "testdata-type-uid" }, "description": "asdasdas", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "rgb(31, 120, 193)", - "mode": "fixed" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] + "format": "ms", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true }, "gridPos": { "h": 4, @@ -149,25 +135,37 @@ "y": 0 }, "id": 2, - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "100%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -179,49 +177,41 @@ "scenarioId": "random_walk" } ], + "thresholds": "", "timeShift": "2h", - "type": "stat" + "type": "stat", + "valueFontSize": "120%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { + "autoMigrateFrom": "singlestat", + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "#5794F2", + "#d44a3a" + ], "datasource": { "apiVersion": "v1", "type": "grafana-testdata-datasource", "uid": "testdata-type-uid" }, "description": "asdasdas", - "fieldConfig": { - "defaults": { - "color": { - "fixedColor": "rgb(31, 120, 193)", - "mode": "fixed" - }, - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "ms" - }, - "overrides": [] + "format": "ms", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true }, "gridPos": { "h": 4, @@ -230,25 +220,37 @@ "y": 4 }, "id": 4, - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "100%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": true, + "lineColor": "rgb(31, 120, 193)", + "show": true }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -260,9 +262,19 @@ "scenarioId": "random_walk" } ], + "thresholds": "", "timeShift": "2h", "title": "Panel Title", - "type": "stat" + "type": "stat", + "valueFontSize": "120%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { "datasource": { @@ -404,43 +416,27 @@ "type": "text" }, { + "autoMigrateFrom": "singlestat", + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], "datasource": { "apiVersion": "v1", "type": "grafana-testdata-datasource", "uid": "testdata-type-uid" }, "description": "asdasdas", - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "max": 100, - "min": 0, - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": true, + "thresholdLabels": false, + "thresholdMarkers": true }, "gridPos": { "h": 4, @@ -449,25 +445,38 @@ "y": 8 }, "id": 10, - "maxDataPoints": 100, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "pluginVersion": "6.2.0-pre", + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -479,8 +488,18 @@ "scenarioId": "random_walk" } ], + "thresholds": "", "timeShift": "2h", - "type": "stat" + "type": "stat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { "datasource": { diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-table/table_tests.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-table/table_tests.v42.json index bdac0888407..f2f7abc07d6 100644 --- a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-table/table_tests.v42.json +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-table/table_tests.v42.json @@ -20,150 +20,12 @@ "links": [], "panels": [ { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "type": "grafana-testdata-datasource" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ColorCell" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-background" - } - }, - { - "id": "custom.align" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.9)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 5 - }, - { - "color": "rgba(50, 172, 45, 0.97)", - "value": 10 - } - ] - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ColorValue" - }, - "properties": [ - { - "id": "unit", - "value": "Bps" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-text" - } - }, - { - "id": "custom.align" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.9)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 5 - }, - { - "color": "rgba(50, 172, 45, 0.97)", - "value": 10 - } - ] - } - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 11, "w": 12, @@ -171,11 +33,76 @@ "y": 0 }, "id": 3, - "options": { - "cellHeight": "sm", - "showHeader": true + "pageSize": 10, + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colorMode": "cell", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "ColorCell", + "thresholds": [ + "5", + "10" + ], + "type": "number", + "unit": "currencyUSD" + }, + { + "alias": "", + "align": "auto", + "colorMode": "value", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "ColorValue", + "thresholds": [ + "5", + "10" + ], + "type": "number", + "unit": "Bps" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "server1", @@ -200,161 +127,29 @@ } ], "title": "Time series to rows (2 pages)", - "transformations": [ - { - "id": "seriesToRows", - "options": { - "reducers": [] - } - } - ], + "transform": "timeseries_to_rows", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [ + { + "text": "Avg", + "value": "avg" + }, + { + "text": "Max", + "value": "max" + }, + { + "text": "Current", + "value": "current" + } + ], "datasource": { "type": "grafana-testdata-datasource" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ColorCell" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-background" - } - }, - { - "id": "custom.align" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.9)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 5 - }, - { - "color": "rgba(50, 172, 45, 0.97)", - "value": 10 - } - ] - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ColorValue" - }, - "properties": [ - { - "id": "unit", - "value": "Bps" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-text" - } - }, - { - "id": "custom.align" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.9)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 5 - }, - { - "color": "rgba(50, 172, 45, 0.97)", - "value": 10 - } - ] - } - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 11, "w": 12, @@ -362,11 +157,76 @@ "y": 0 }, "id": 4, - "options": { - "cellHeight": "sm", - "showHeader": true + "pageSize": 10, + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colorMode": "cell", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "ColorCell", + "thresholds": [ + "5", + "10" + ], + "type": "number", + "unit": "currencyUSD" + }, + { + "alias": "", + "align": "auto", + "colorMode": "value", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "ColorValue", + "thresholds": [ + "5", + "10" + ], + "type": "number", + "unit": "Bps" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "server1", @@ -391,121 +251,16 @@ } ], "title": "Time series aggregations", - "transformations": [ - { - "id": "reduce", - "options": { - "includeTimeField": false, - "reducers": [ - "mean", - "max", - "lastNotNull" - ] - } - } - ], + "transform": "timeseries_aggregations", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "type": "grafana-testdata-datasource" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/Color/" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-background" - } - }, - { - "id": "custom.align" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.9)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 5 - }, - { - "color": "rgba(50, 172, 45, 0.97)", - "value": 10 - } - ] - } - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 7, "w": 24, @@ -513,11 +268,55 @@ "y": 11 }, "id": 5, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colorMode": "row", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "/Color/", + "thresholds": [ + "5", + "10" + ], + "type": "number", + "unit": "currencyUSD" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "ColorValue", @@ -533,161 +332,16 @@ } ], "title": "color row by threshold", - "transformations": [ - { - "id": "seriesToColumns", - "options": { - "reducers": [] - } - } - ], + "transform": "timeseries_to_columns", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "type": "grafana-testdata-datasource" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ColorCell" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-background" - } - }, - { - "id": "custom.align" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.9)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 5 - }, - { - "color": "rgba(50, 172, 45, 0.97)", - "value": 10 - } - ] - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ColorValue" - }, - "properties": [ - { - "id": "unit", - "value": "Bps" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-text" - } - }, - { - "id": "custom.align" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.9)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.89)", - "value": 5 - }, - { - "color": "rgba(50, 172, 45, 0.97)", - "value": 10 - } - ] - } - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 8, "w": 24, @@ -695,11 +349,75 @@ "y": 18 }, "id": 2, - "options": { - "cellHeight": "sm", - "showHeader": true + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colorMode": "cell", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "ColorCell", + "thresholds": [ + "5", + "10" + ], + "type": "number", + "unit": "currencyUSD" + }, + { + "alias": "", + "align": "auto", + "colorMode": "value", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "mappingType": 1, + "pattern": "ColorValue", + "thresholds": [ + "5", + "10" + ], + "type": "number", + "unit": "Bps" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "ColorValue", @@ -724,181 +442,16 @@ } ], "title": "Column style thresholds \u0026 units", - "transformations": [ - { - "id": "seriesToColumns", - "options": { - "reducers": [] - } - } - ], + "transform": "timeseries_to_columns", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [], "datasource": { "type": "grafana-testdata-datasource" }, - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 2, - "displayName": "", - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "displayName", - "value": "Time" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - }, - { - "id": "custom.align" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ColorCell" - }, - "properties": [ - { - "id": "unit", - "value": "currencyUSD" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "", - "url": "http://www.grafana.com" - } - ] - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-background" - } - }, - { - "id": "custom.align" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.5)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.5)", - "value": 5 - }, - { - "color": "rgba(50, 172, 45, 0.5)", - "value": 10 - } - ] - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ColorValue" - }, - "properties": [ - { - "id": "unit", - "value": "Bps" - }, - { - "id": "decimals", - "value": 2 - }, - { - "id": "links", - "value": [ - { - "targetBlank": false, - "title": "", - "url": "http://www.grafana.com" - } - ] - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-text" - } - }, - { - "id": "custom.align" - }, - { - "id": "thresholds", - "value": { - "mode": "absolute", - "steps": [ - { - "color": "rgba(245, 54, 54, 0.5)", - "value": null - }, - { - "color": "rgba(237, 129, 40, 0.5)", - "value": 5 - }, - { - "color": "rgba(50, 172, 45, 0.5)", - "value": 10 - } - ] - } - } - ] - } - ] - }, + "fontSize": "100%", "gridPos": { "h": 10, "w": 24, @@ -906,11 +459,82 @@ "y": 26 }, "id": 6, - "options": { - "cellHeight": "sm", - "showHeader": true + "pageSize": 20, + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true }, - "pluginVersion": "12.1.0", + "styles": [ + { + "alias": "Time", + "align": "auto", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "align": "auto", + "colorMode": "cell", + "colors": [ + "rgba(245, 54, 54, 0.5)", + "rgba(237, 129, 40, 0.5)", + "rgba(50, 172, 45, 0.5)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "link": true, + "linkTargetBlank": true, + "linkTooltip": "", + "linkUrl": "http://www.grafana.com", + "mappingType": 1, + "pattern": "ColorCell", + "thresholds": [ + "5", + "10" + ], + "type": "number", + "unit": "currencyUSD" + }, + { + "alias": "", + "align": "auto", + "colorMode": "value", + "colors": [ + "rgba(245, 54, 54, 0.5)", + "rgba(237, 129, 40, 0.5)", + "rgba(50, 172, 45, 0.5)" + ], + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "decimals": 2, + "link": true, + "linkUrl": "http://www.grafana.com", + "mappingType": 1, + "pattern": "ColorValue", + "thresholds": [ + "5", + "10" + ], + "type": "number", + "unit": "Bps" + }, + { + "alias": "", + "align": "auto", + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], "targets": [ { "alias": "ColorValue", @@ -935,14 +559,7 @@ } ], "title": "Column style thresholds and links", - "transformations": [ - { - "id": "seriesToColumns", - "options": { - "reducers": [] - } - } - ], + "transform": "timeseries_to_columns", "type": "table" } ], diff --git a/apps/dashboard/pkg/migration/testdata/input/v10.table_thresholds.json b/apps/dashboard/pkg/migration/testdata/input/v10.table_thresholds.json new file mode 100644 index 00000000000..065423b9fea --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v10.table_thresholds.json @@ -0,0 +1,36 @@ +{ + "title": "V10 Table Thresholds Test", + "schemaVersion": 9, + "panels": [ + { + "id": 1, + "type": "table", + "styles": [ + { + "thresholds": ["10", "20", "30"] + }, + { + "thresholds": ["100", "200", "300"] + } + ] + }, + { + "id": 2, + "type": "table", + "styles": [ + { + "thresholds": ["50", "75"] + } + ] + }, + { + "id": 3, + "type": "graph", + "styles": [ + { + "thresholds": ["5", "10", "15"] + } + ] + } + ] +} diff --git a/apps/dashboard/pkg/migration/testdata/input/v11.no-op-migration.json b/apps/dashboard/pkg/migration/testdata/input/v11.no-op-migration.json new file mode 100644 index 00000000000..82b054d9d11 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v11.no-op-migration.json @@ -0,0 +1,58 @@ +{ + "title": "V11 No-Op Migration Test Dashboard", + "schemaVersion": 10, + "panels": [ + { + "type": "graph", + "title": "CPU Usage", + "id": 1, + "yAxes": [ + { + "show": true, + "min": null, + "max": null + } + ] + }, + { + "type": "singlestat", + "title": "Memory Usage", + "id": 2, + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ] + } + ], + "templating": { + "list": [ + { + "name": "server", + "type": "query", + "datasource": "prometheus", + "query": "label_values(server)", + "refresh": 1, + "options": [] + } + ] + }, + "annotations": { + "list": [ + { + "name": "Annotations & Alerts", + "datasource": "grafana", + "enable": true + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] + } + } diff --git a/apps/dashboard/pkg/migration/testdata/input/v12.template-variables.json b/apps/dashboard/pkg/migration/testdata/input/v12.template-variables.json new file mode 100644 index 00000000000..e851b6908a7 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v12.template-variables.json @@ -0,0 +1,38 @@ +{ + "title": "V12 Template Variables Migration Test", + "schemaVersion": 11, + "templating": { + "list": [ + { + "type": "query", + "name": "refresh_true_var", + "refresh": true + }, + { + "type": "query", + "name": "refresh_false_var", + "refresh": false + }, + { + "type": "query", + "name": "hide_variable_var", + "hideVariable": true + }, + { + "type": "query", + "name": "hide_label_var", + "hideLabel": true + }, + { + "type": "query", + "name": "priority_var", + "hideVariable": true, + "hideLabel": true + }, + { + "type": "query", + "name": "no_properties_var" + } + ] + } + } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/input/v13.graph_thresholds.json b/apps/dashboard/pkg/migration/testdata/input/v13.graph_thresholds.json new file mode 100644 index 00000000000..27a83b9a232 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v13.graph_thresholds.json @@ -0,0 +1,62 @@ +{ + "title": "V13 Graph Thresholds Migration Test", + "schemaVersion": 12, + "panels": [ + { + "type": "graph", + "id": 1, + "title": "Graph with Line Thresholds", + "grid": { + "threshold1": 200, + "threshold2": 400, + "threshold1Color": "yellow", + "threshold2Color": "red", + "thresholdLine": true + } + }, + { + "type": "graph", + "id": 2, + "title": "Graph with Fill Thresholds", + "grid": { + "threshold1": 100, + "threshold2": 300, + "threshold1Color": "green", + "threshold2Color": "blue", + "thresholdLine": false + } + }, + { + "type": "graph", + "id": 3, + "title": "Graph with Single Threshold", + "grid": { + "threshold1": 150, + "threshold1Color": "orange", + "thresholdLine": true + } + }, + { + "type": "graph", + "id": 4, + "title": "Graph with Existing Thresholds", + "thresholds": [ + { + "value": 50, + "color": "purple", + "colorMode": "custom" + } + ], + "grid": { + "threshold1": 200, + "threshold1Color": "yellow", + "thresholdLine": false + } + }, + { + "type": "singlestat", + "id": 5, + "title": "Non-Graph Panel" + } + ] +} diff --git a/apps/dashboard/pkg/migration/testdata/input/v13.minimal_graph_config.json b/apps/dashboard/pkg/migration/testdata/input/v13.minimal_graph_config.json new file mode 100644 index 00000000000..9911e340073 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v13.minimal_graph_config.json @@ -0,0 +1,34 @@ +{ + "title": "Dashboard with minimal graph panel settings", + "tags": [], + "timezone": "browser", + "editable": true, + "panels": [ + { + "id": 4, + "type": "graph", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "editorMode": "builder", + "expr": "{\"a.utf8.metric 🤘\", job=\"prometheus-utf8\"}", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ] + } + ], + "schemaVersion": 12, + "time": { + "from": "now-6h", + "to": "now" + }, + "templating": { + "list": [] + } +} diff --git a/apps/dashboard/pkg/migration/testdata/input/v7.timepicker.json b/apps/dashboard/pkg/migration/testdata/input/v7.timepicker.json new file mode 100644 index 00000000000..6bd43ba7e34 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v7.timepicker.json @@ -0,0 +1,23 @@ +{ + "schemaVersion": 6, + "nav": [ + { + "enable": true, + "type": "timepicker", + "status": "Stable", + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"], + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], + "now": true, + "collapse": false, + "notice": false + } + ], + "panels": [ + { + "targets": [ + {"expr": "up"}, + {"expr": "cpu_usage", "refId": "B"} + ] + } + ] +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/input/v8.influxdb_query.json b/apps/dashboard/pkg/migration/testdata/input/v8.influxdb_query.json new file mode 100644 index 00000000000..5cad1ac187a --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v8.influxdb_query.json @@ -0,0 +1,67 @@ +{ + "title": "V8 InfluxDB Query Test", + "schemaVersion": 7, + "panels": [ + { + "id": 1, + "type": "graph", + "targets": [ + { + "fields": [ + { + "name": "value", + "func": "mean", + "mathExpr": "*2", + "asExpr": "doubled" + } + ], + "tags": [ + { + "key": "host", + "value": "server1" + } + ], + "groupBy": [ + { + "type": "time", + "interval": "1m" + }, + { + "type": "tag", + "key": "host" + } + ], + "fill": "null" + } + ] + }, + { + "id": 2, + "type": "graph", + "targets": [ + { + "rawQuery": true, + "fields": [ + { + "name": "value", + "func": "mean" + } + ], + "tags": [ + { + "key": "host", + "value": "server1" + } + ], + "groupBy": [ + { + "type": "time", + "interval": "1m" + } + ], + "fill": "null" + } + ] + } + ] +} diff --git a/apps/dashboard/pkg/migration/testdata/input/v9.no-op.json b/apps/dashboard/pkg/migration/testdata/input/v9.no-op.json new file mode 100644 index 00000000000..51f9dca7b59 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/input/v9.no-op.json @@ -0,0 +1,18 @@ +{ + "title": "V9 No-Op Migration Test", + "schemaVersion": 8, + "panels": [ + { + "id": 1, + "type": "singlestat" + }, + { + "id": 2, + "type": "singlestat" + }, + { + "id": 3, + "type": "graph" + } + ] + } \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v10.table_thresholds.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v10.table_thresholds.v42.json new file mode 100644 index 00000000000..3f17bcd374c --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v10.table_thresholds.v42.json @@ -0,0 +1,132 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "autoMigrateFrom": "table-old", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 1, + "styles": [ + { + "align": "auto", + "thresholds": [ + "20", + "30" + ] + }, + { + "align": "auto", + "thresholds": [ + "200", + "300" + ] + } + ], + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "type": "table" + }, + { + "autoMigrateFrom": "table-old", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 2, + "styles": [ + { + "align": "auto", + "thresholds": [ + "50", + "75" + ] + } + ], + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "type": "table" + }, + { + "autoMigrateFrom": "graph", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 3, + "styles": [ + { + "thresholds": [ + "5", + "10", + "15" + ] + } + ], + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "V10 Table Thresholds Test", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v11.no-op-migration.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v11.no-op-migration.v42.json new file mode 100644 index 00000000000..cde0f37b8a3 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v11.no-op-migration.v42.json @@ -0,0 +1,121 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + }, + { + "datasource": { + "uid": "grafana" + }, + "enable": true, + "name": "Annotations \u0026 Alerts" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "autoMigrateFrom": "graph", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 1, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "CPU Usage", + "type": "timeseries", + "yAxes": [ + { + "show": true + } + ] + }, + { + "autoMigrateFrom": "singlestat", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 2, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Memory Usage", + "type": "stat", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ] + } + ], + "refresh": "", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [ + { + "datasource": "prometheus", + "name": "server", + "options": [], + "query": "label_values(server)", + "refresh": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "V11 No-Op Migration Test Dashboard", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v12.template-variables.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v12.template-variables.v42.json new file mode 100644 index 00000000000..85b7de371d5 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v12.template-variables.v42.json @@ -0,0 +1,81 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [], + "refresh": "", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [ + { + "name": "refresh_true_var", + "options": [], + "refresh": 1, + "type": "query" + }, + { + "name": "refresh_false_var", + "options": [], + "refresh": 1, + "type": "query" + }, + { + "hide": 2, + "hideVariable": true, + "name": "hide_variable_var", + "options": [], + "refresh": 1, + "type": "query" + }, + { + "hide": 1, + "hideLabel": true, + "name": "hide_label_var", + "options": [], + "refresh": 1, + "type": "query" + }, + { + "hide": 2, + "hideLabel": true, + "hideVariable": true, + "name": "priority_var", + "options": [], + "refresh": 1, + "type": "query" + }, + { + "name": "no_properties_var", + "options": [], + "refresh": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "V12 Template Variables Migration Test", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v13.graph_thresholds.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v13.graph_thresholds.v42.json new file mode 100644 index 00000000000..c799c1cb6b8 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v13.graph_thresholds.v42.json @@ -0,0 +1,174 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "autoMigrateFrom": "graph", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "grid": { + "threshold1": 200, + "threshold1Color": "yellow", + "threshold2": 400, + "threshold2Color": "red", + "thresholdLine": true + }, + "id": 1, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Graph with Line Thresholds", + "type": "timeseries" + }, + { + "autoMigrateFrom": "graph", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "grid": { + "threshold1": 100, + "threshold1Color": "green", + "threshold2": 300, + "threshold2Color": "blue", + "thresholdLine": false + }, + "id": 2, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Graph with Fill Thresholds", + "type": "timeseries" + }, + { + "autoMigrateFrom": "graph", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "grid": { + "threshold1": 150, + "threshold1Color": "orange", + "thresholdLine": true + }, + "id": 3, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Graph with Single Threshold", + "type": "timeseries" + }, + { + "autoMigrateFrom": "graph", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "grid": { + "threshold1": 200, + "threshold1Color": "yellow", + "thresholdLine": false + }, + "id": 4, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "thresholds": [ + { + "color": "purple", + "colorMode": "custom", + "value": 50 + } + ], + "title": "Graph with Existing Thresholds", + "type": "timeseries" + }, + { + "autoMigrateFrom": "singlestat", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 5, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "title": "Non-Graph Panel", + "type": "stat" + } + ], + "refresh": "", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "V13 Graph Thresholds Migration Test", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v13.minimal_graph_config.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v13.minimal_graph_config.v42.json new file mode 100644 index 00000000000..e40a580b6ed --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v13.minimal_graph_config.v42.json @@ -0,0 +1,61 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "autoMigrateFrom": "graph", + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "id": 4, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "editorMode": "builder", + "expr": "{\"a.utf8.metric 🤘\", job=\"prometheus-utf8\"}", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Dashboard with minimal graph panel settings", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v15.no-op-migration.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v15.no-op-migration.v42.json index 05bac4cb246..1cda544d3fd 100644 --- a/apps/dashboard/pkg/migration/testdata/output/latest_version/v15.no-op-migration.v42.json +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v15.no-op-migration.v42.json @@ -54,59 +54,13 @@ ] }, { + "autoMigrateFrom": "singlestat", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, "id": 2, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -118,7 +72,14 @@ } ], "title": "Memory Usage", - "type": "stat" + "type": "stat", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ] } ], "refresh": "", diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v16.grid_layout_upgrade.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v16.grid_layout_upgrade.v42.json index 31cb9895cf4..e9d3b12001d 100644 --- a/apps/dashboard/pkg/migration/testdata/output/latest_version/v16.grid_layout_upgrade.v42.json +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v16.grid_layout_upgrade.v42.json @@ -75,30 +75,12 @@ "type": "timeseries" }, { + "autoMigrateFrom": "singlestat", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, "gridPos": { "h": 7, "w": 12, @@ -106,24 +88,6 @@ "y": 1 }, "id": 2, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", "targets": [ { "datasource": { diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v17.minspan_to_maxperrow.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v17.minspan_to_maxperrow.v42.json index f52bfb99cf5..9b0ddbb85df 100644 --- a/apps/dashboard/pkg/migration/testdata/output/latest_version/v17.minspan_to_maxperrow.v42.json +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v17.minspan_to_maxperrow.v42.json @@ -77,30 +77,12 @@ "type": "timeseries" }, { + "autoMigrateFrom": "singlestat", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, "gridPos": { "h": 4, "w": 6, @@ -108,24 +90,7 @@ "y": 8 }, "id": 2, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "maxPerRow": 6, "targets": [ { "datasource": { diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v19.panel_links.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v19.panel_links.v42.json index d92b346e5a6..51072542d22 100644 --- a/apps/dashboard/pkg/migration/testdata/output/latest_version/v19.panel_links.v42.json +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v19.panel_links.v42.json @@ -153,49 +153,13 @@ "type": "gauge" }, { + "autoMigrateFrom": "singlestat", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, "id": 6, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", "targets": [ { "datasource": { diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v22.table_panel_align.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v22.table_panel_align.v42.json index 22524901c13..d284282e97c 100644 --- a/apps/dashboard/pkg/migration/testdata/output/latest_version/v22.table_panel_align.v42.json +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v22.table_panel_align.v42.json @@ -21,69 +21,25 @@ "links": [], "panels": [ { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "custom.align" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Value" - }, - "properties": [ - { - "id": "custom.align" - } - ] - } - ] - }, "id": 1, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "align": "auto", + "pattern": "Time", + "type": "number" + }, + { + "align": "auto", + "pattern": "Value", + "type": "string" + } + ], "targets": [ { "datasource": { diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v24.table-angular.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v24.table-angular.v42.json index 420965dffb4..443ad1f6eb1 100644 --- a/apps/dashboard/pkg/migration/testdata/output/latest_version/v24.table-angular.v42.json +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v24.table-angular.v42.json @@ -21,54 +21,30 @@ "links": [], "panels": [ { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests basic migration with default style pattern (/.*/) containing thresholds and colors. Should convert styles to fieldConfig.defaults with threshold steps.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "yellow", - "value": 10 - }, - { - "color": "green", - "value": 20 - }, - { - "value": 30 - } - ] - } - }, - "overrides": [] - }, "id": 1, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "legend": true, + "styles": [ + { + "colors": [ + "red", + "yellow", + "green" + ], + "pattern": "/.*/", + "thresholds": [ + "10", + "20", + "30" + ] + } + ], "targets": [ { "datasource": { @@ -91,136 +67,58 @@ "type": "table" }, { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests comprehensive migration including: default style with thresholds/colors/unit/decimals/align/colorMode, column overrides with exact name and regex patterns, date formatting, hidden columns, and links with tooltips.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "center", - "cellOptions": { - "type": "color-background" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, + "id": 2, + "styles": [ + { + "align": "center", + "colorMode": "cell", + "colors": [ + "green", + "yellow", + "red" + ], "decimals": 2, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "yellow", - "value": 100 - }, - { - "color": "red", - "value": 500 - } - ] - }, + "pattern": "/.*/", + "thresholds": [ + "100", + "500" + ], "unit": "bytes" }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Status" - }, - "properties": [ - { - "id": "displayName", - "value": "Current Status" - }, - { - "id": "unit", - "value": "short" - }, - { - "id": "decimals", - "value": 0 - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-text" - } - }, - { - "id": "custom.align", - "value": "left" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/Error.*/" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "View error details", - "url": "http://example.com/errors" - } - ] - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-background" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "displayName", - "value": "Timestamp" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hidden" - }, - "properties": [ - { - "id": "custom.hideFrom.viz", - "value": true - } - ] - } - ] - }, - "id": 2, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + { + "alias": "Current Status", + "align": "left", + "colorMode": "value", + "decimals": 0, + "pattern": "Status", + "unit": "short" + }, + { + "colorMode": "row", + "link": true, + "linkTargetBlank": true, + "linkTooltip": "View error details", + "linkUrl": "http://example.com/errors", + "pattern": "/Error.*/" + }, + { + "alias": "Timestamp", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "pattern": "Hidden", + "type": "hidden" + } + ], "targets": [ { "datasource": { @@ -235,49 +133,47 @@ "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [ + { + "text": "Average", + "value": "avg" + }, + { + "text": "Maximum", + "value": "max" + }, + { + "text": "Minimum", + "value": "min" + }, + { + "text": "Total", + "value": "total" + }, + { + "text": "Current", + "value": "current" + }, + { + "text": "Count", + "value": "count" + } + ], "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests migration of timeseries_aggregations transform to reduce transformation with column mappings (avg-\u003emean, max-\u003emax, min-\u003emin, total-\u003esum, current-\u003elastNotNull, count-\u003ecount).", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 1, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, "id": 3, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "decimals": 1, + "pattern": "/.*/", + "unit": "percent" + } + ], "targets": [ { "datasource": { @@ -289,67 +185,24 @@ } ], "title": "Table with Timeseries Aggregations Transform", - "transformations": [ - { - "id": "reduce", - "options": { - "includeTimeField": false, - "reducers": [ - "mean", - "max", - "min", - "sum", - "lastNotNull", - "count" - ] - } - } - ], + "transform": "timeseries_aggregations", "type": "table" }, { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests migration of timeseries_to_rows transform to seriesToRows transformation.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, "id": 4, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "pattern": "/.*/", + "unit": "short" + } + ], "targets": [ { "datasource": { @@ -361,59 +214,24 @@ } ], "title": "Table with Timeseries to Rows Transform", - "transformations": [ - { - "id": "seriesToRows", - "options": { - "reducers": [] - } - } - ], + "transform": "timeseries_to_rows", "type": "table" }, { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests migration of timeseries_to_columns transform to seriesToColumns transformation.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, "id": 5, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "pattern": "/.*/", + "unit": "bytes" + } + ], "targets": [ { "datasource": { @@ -425,57 +243,24 @@ } ], "title": "Table with Timeseries to Columns Transform", - "transformations": [ - { - "id": "seriesToColumns", - "options": { - "reducers": [] - } - } - ], + "transform": "timeseries_to_columns", "type": "table" }, { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests migration of table transform to merge transformation. Also tests auto alignment conversion to empty string.", - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, "id": 6, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "align": "auto", + "pattern": "/.*/" + } + ], "targets": [ { "datasource": { @@ -487,59 +272,24 @@ } ], "title": "Table with Merge Transform", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], + "transform": "table", "type": "table" }, { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests that existing transformations are preserved and new transformation from old format is appended to the list.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, "id": 7, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "pattern": "/.*/", + "unit": "short" + } + ], "targets": [ { "datasource": { @@ -551,6 +301,7 @@ } ], "title": "Table with Existing Transformations", + "transform": "timeseries_to_rows", "transformations": [ { "id": "filterFieldsByName", @@ -562,66 +313,35 @@ ] } } - }, - { - "id": "seriesToRows", - "options": { - "reducers": [] - } } ], "type": "table" }, { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests handling of mixed numeric and string threshold values (int, string, float) with proper type conversion.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "yellow", - "value": 10 - }, - { - "color": "orange", - "value": 20 - }, - { - "color": "red", - "value": 30.5 - } - ] - } - }, - "overrides": [] - }, "id": 8, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "colors": [ + "green", + "yellow", + "orange", + "red" + ], + "pattern": "/.*/", + "thresholds": [ + 10, + "20", + 30.5 + ] + } + ], "targets": [ { "datasource": { @@ -636,90 +356,32 @@ "type": "table" }, { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests all color mode mappings: cell-\u003ecolor-background, row-\u003ecolor-background, value-\u003ecolor-text.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "color-background" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "CellColumn" - }, - "properties": [ - { - "id": "custom.cellOptions", - "value": { - "type": "color-background" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RowColumn" - }, - "properties": [ - { - "id": "custom.cellOptions", - "value": { - "type": "color-background" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ValueColumn" - }, - "properties": [ - { - "id": "custom.cellOptions", - "value": { - "type": "color-text" - } - } - ] - } - ] - }, "id": 9, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "colorMode": "cell", + "pattern": "/.*/" + }, + { + "colorMode": "cell", + "pattern": "CellColumn" + }, + { + "colorMode": "row", + "pattern": "RowColumn" + }, + { + "colorMode": "value", + "pattern": "ValueColumn" + } + ], "targets": [ { "datasource": { @@ -734,83 +396,32 @@ "type": "table" }, { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests all alignment options: left, center, right, and auto (should convert to empty string).", - "fieldConfig": { - "defaults": { - "custom": { - "align": "center", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "LeftColumn" - }, - "properties": [ - { - "id": "custom.align", - "value": "left" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RightColumn" - }, - "properties": [ - { - "id": "custom.align", - "value": "right" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "AutoColumn" - }, - "properties": [ - { - "id": "custom.align" - } - ] - } - ] - }, "id": 10, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "align": "center", + "pattern": "/.*/" + }, + { + "align": "left", + "pattern": "LeftColumn" + }, + { + "align": "right", + "pattern": "RightColumn" + }, + { + "align": "auto", + "pattern": "AutoColumn" + } + ], "targets": [ { "datasource": { @@ -825,97 +436,36 @@ "type": "table" }, { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests both field matcher types: byName for exact matches and byRegexp for regex patterns.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, + "id": 11, + "styles": [ + { + "pattern": "/.*/", "unit": "short" }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "ExactColumnName" - }, - "properties": [ - { - "id": "displayName", - "value": "Exact Match" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/Regex.*Pattern/" - }, - "properties": [ - { - "id": "displayName", - "value": "Regex Match" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/^Start/" - }, - "properties": [ - { - "id": "displayName", - "value": "Start Pattern" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/End$/" - }, - "properties": [ - { - "id": "displayName", - "value": "End Pattern" - } - ] - } - ] - }, - "id": 11, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + { + "alias": "Exact Match", + "pattern": "ExactColumnName" + }, + { + "alias": "Regex Match", + "pattern": "/Regex.*Pattern/" + }, + { + "alias": "Start Pattern", + "pattern": "/^Start/" + }, + { + "alias": "End Pattern", + "pattern": "/End$/" + } + ], "targets": [ { "datasource": { @@ -930,103 +480,38 @@ "type": "table" }, { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests various link configurations: with and without tooltip, with and without target blank.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, + "id": 12, + "styles": [ + { + "pattern": "/.*/", "unit": "short" }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "LinkWithTooltip" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "Click to view details", - "url": "http://example.com/with-tooltip" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "LinkWithoutTooltip" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": false, - "title": "", - "url": "http://example.com/no-tooltip" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "LinkMinimal" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": false, - "title": "", - "url": "http://example.com/minimal" - } - ] - } - ] - } - ] - }, - "id": 12, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + { + "link": true, + "linkTargetBlank": true, + "linkTooltip": "Click to view details", + "linkUrl": "http://example.com/with-tooltip", + "pattern": "LinkWithTooltip" + }, + { + "link": true, + "linkTargetBlank": false, + "linkUrl": "http://example.com/no-tooltip", + "pattern": "LinkWithoutTooltip" + }, + { + "link": true, + "linkUrl": "http://example.com/minimal", + "pattern": "LinkMinimal" + } + ], "targets": [ { "datasource": { @@ -1041,97 +526,38 @@ "type": "table" }, { + "autoMigrateFrom": "table-old", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Tests various date format patterns and aliases.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, + "id": 13, + "styles": [ + { + "pattern": "/.*/", "unit": "short" }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "DateISO" - }, - "properties": [ - { - "id": "displayName", - "value": "ISO Date" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "DateTime" - }, - "properties": [ - { - "id": "displayName", - "value": "Full DateTime" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "TimeOnly" - }, - "properties": [ - { - "id": "displayName", - "value": "Time Only" - }, - { - "id": "unit", - "value": "time: HH:mm:ss" - } - ] - } - ] - }, - "id": 13, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + { + "alias": "ISO Date", + "dateFormat": "YYYY-MM-DD", + "pattern": "DateISO", + "type": "date" + }, + { + "alias": "Full DateTime", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "DateTime", + "type": "date" + }, + { + "alias": "Time Only", + "dateFormat": "HH:mm:ss", + "pattern": "TimeOnly", + "type": "date" + } + ], "targets": [ { "datasource": { @@ -1226,50 +652,14 @@ "type": "timeseries" }, { + "autoMigrateFrom": "singlestat", "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, "description": "Other panel types should not be affected by table migration.", - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, "id": 17, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", "targets": [ { "datasource": { diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v28.singlestat_and_variable_properties.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v28.singlestat_and_variable_properties.v42.json index 51ee2142199..efacbd5dc24 100644 --- a/apps/dashboard/pkg/migration/testdata/output/latest_version/v28.singlestat_and_variable_properties.v42.json +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v28.singlestat_and_variable_properties.v42.json @@ -21,53 +21,23 @@ "links": [], "panels": [ { + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "grid": { + "max": 10, + "min": 1 }, "id": 1, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "legend": true, "targets": [ { "datasource": { @@ -86,56 +56,31 @@ "refId": "B" } ], + "thresholds": "10,20,30", "type": "stat" }, { + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "gauge": { + "show": true, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "grid": { + "max": 10, + "min": 1 }, "id": 2, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -146,82 +91,33 @@ "refId": "A" } ], + "thresholds": "10,20,30", "type": "stat" }, { + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "20": { - "text": "test" - } - }, - "type": "value" - }, - { - "options": { - "30": { - "text": "test1" - } - }, - "type": "value" - }, - { - "options": { - "40": { - "color": "orange", - "text": "50" - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "grid": { + "max": 10, + "min": 1 }, "id": 3, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "legend": true, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + } + ], "targets": [ { "datasource": { @@ -240,7 +136,25 @@ "refId": "B" } ], - "type": "stat" + "thresholds": "10,20,30", + "type": "stat", + "valueMaps": [ + { + "op": "=", + "text": "test", + "value": "20" + }, + { + "op": "=", + "text": "test1", + "value": "30" + }, + { + "op": "=", + "text": "50", + "value": "40" + } + ] }, { "datasource": { diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v28.singlestat_migration.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v28.singlestat_migration.v42.json index d541cf4751b..c08136b1926 100644 --- a/apps/dashboard/pkg/migration/testdata/output/latest_version/v28.singlestat_migration.v42.json +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v28.singlestat_migration.v42.json @@ -21,53 +21,23 @@ "links": [], "panels": [ { + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "grid": { + "max": 10, + "min": 1 }, "id": 1, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "legend": true, "targets": [ { "datasource": { @@ -86,52 +56,27 @@ "refId": "B" } ], + "thresholds": "10,20,30", "type": "stat" }, { + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] + "grid": { + "max": 10, + "min": 1 }, "id": 2, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "legend": true, "targets": [ { "datasource": { @@ -150,56 +95,31 @@ "refId": "B" } ], + "thresholds": "", "type": "stat" }, { + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "gauge": { + "show": true, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "grid": { + "max": 10, + "min": 1 }, "id": 3, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", "targets": [ { "datasource": { @@ -210,82 +130,33 @@ "refId": "A" } ], + "thresholds": "10,20,30", "type": "stat" }, { + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], "datasource": { "apiVersion": "v1", "type": "prometheus", "uid": "default-ds-uid" }, - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "20": { - "text": "test" - } - }, - "type": "value" - }, - { - "options": { - "30": { - "text": "test1" - } - }, - "type": "value" - }, - { - "options": { - "40": { - "color": "orange", - "text": "50" - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "grid": { + "max": 10, + "min": 1 }, "id": 4, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "legend": true, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + } + ], "targets": [ { "datasource": { @@ -304,7 +175,25 @@ "refId": "B" } ], - "type": "stat" + "thresholds": "10,20,30", + "type": "stat", + "valueMaps": [ + { + "op": "=", + "text": "test", + "value": "20" + }, + { + "op": "=", + "text": "test1", + "value": "30" + }, + { + "op": "=", + "text": "50", + "value": "40" + } + ] }, { "datasource": { @@ -327,38 +216,24 @@ "type": "timeseries" }, { + "autoMigrateFrom": "grafana-singlestat-panel", + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], "datasource": { "type": "prometheus" }, - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "areaF2" - }, - "overrides": [] + "format": "areaF2", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true }, "gridPos": { "h": 8, @@ -367,25 +242,37 @@ "y": 43 }, "id": 5, - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "postfix": "b", + "postfixFontSize": "50%", + "prefix": "a", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -395,8 +282,18 @@ "refId": "A" } ], + "thresholds": "", "title": "grafana-singlestat-panel", - "type": "stat" + "type": "stat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { "datasource": { diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v7.timepicker.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v7.timepicker.v42.json new file mode 100644 index 00000000000..15e27073fe7 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v7.timepicker.v42.json @@ -0,0 +1,85 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 1, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "expr": "up", + "refId": "A" + }, + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "expr": "cpu_usage", + "refId": "B" + } + ] + } + ], + "refresh": "", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "collapse": false, + "enable": true, + "notice": false, + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "status": "Stable", + "type": "timepicker" + }, + "timezone": "", + "title": "No Title", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v8.influxdb_query.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v8.influxdb_query.v42.json new file mode 100644 index 00000000000..307a9d86a96 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v8.influxdb_query.v42.json @@ -0,0 +1,143 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "autoMigrateFrom": "graph", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 1, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + }, + { + "params": [ + "host" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "refId": "A", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + }, + { + "params": [ + "*2" + ], + "type": "math" + }, + { + "params": [ + "doubled" + ], + "type": "alias" + } + ] + ], + "tags": [ + { + "key": "host", + "value": "server1" + } + ] + } + ], + "type": "timeseries" + }, + { + "autoMigrateFrom": "graph", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 2, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "groupBy": [ + { + "interval": "1m", + "type": "time" + } + ], + "rawQuery": true, + "refId": "A", + "tags": [ + { + "key": "host", + "value": "server1" + } + ] + } + ], + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "V8 InfluxDB Query Test", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/latest_version/v9.no-op.v42.json b/apps/dashboard/pkg/migration/testdata/output/latest_version/v9.no-op.v42.json new file mode 100644 index 00000000000..12d4a1383e0 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/latest_version/v9.no-op.v42.json @@ -0,0 +1,98 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "autoMigrateFrom": "singlestat", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 1, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "type": "stat" + }, + { + "autoMigrateFrom": "singlestat", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 2, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "type": "stat" + }, + { + "autoMigrateFrom": "graph", + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "id": 3, + "targets": [ + { + "datasource": { + "apiVersion": "v1", + "type": "prometheus", + "uid": "default-ds-uid" + }, + "refId": "A" + } + ], + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "V9 No-Op Migration Test", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/single_version/v10.table_thresholds.v10.json b/apps/dashboard/pkg/migration/testdata/output/single_version/v10.table_thresholds.v10.json new file mode 100644 index 00000000000..2e178e2e129 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/single_version/v10.table_thresholds.v10.json @@ -0,0 +1,81 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "id": 1, + "styles": [ + { + "thresholds": [ + "20", + "30" + ] + }, + { + "thresholds": [ + "200", + "300" + ] + } + ], + "type": "table" + }, + { + "id": 2, + "styles": [ + { + "thresholds": [ + "50", + "75" + ] + } + ], + "type": "table" + }, + { + "autoMigrateFrom": "graph", + "id": 3, + "styles": [ + { + "thresholds": [ + "5", + "10", + "15" + ] + } + ], + "type": "timeseries" + } + ], + "schemaVersion": 10, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "V10 Table Thresholds Test", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/single_version/v11.no-op-migration.v11.json b/apps/dashboard/pkg/migration/testdata/output/single_version/v11.no-op-migration.v11.json new file mode 100644 index 00000000000..7e6680140e0 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/single_version/v11.no-op-migration.v11.json @@ -0,0 +1,88 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + }, + { + "datasource": "grafana", + "enable": true, + "name": "Annotations \u0026 Alerts" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "autoMigrateFrom": "graph", + "id": 1, + "title": "CPU Usage", + "type": "timeseries", + "yAxes": [ + { + "show": true + } + ] + }, + { + "autoMigrateFrom": "singlestat", + "id": 2, + "title": "Memory Usage", + "type": "stat", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ] + } + ], + "schemaVersion": 11, + "tags": [], + "templating": { + "list": [ + { + "datasource": "prometheus", + "name": "server", + "options": [], + "query": "label_values(server)", + "refresh": 1, + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "V11 No-Op Migration Test Dashboard", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/single_version/v12.template-variables.v12.json b/apps/dashboard/pkg/migration/testdata/output/single_version/v12.template-variables.v12.json new file mode 100644 index 00000000000..b1de134895f --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/single_version/v12.template-variables.v12.json @@ -0,0 +1,75 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [], + "schemaVersion": 12, + "tags": [], + "templating": { + "list": [ + { + "name": "refresh_true_var", + "options": [], + "refresh": 1, + "type": "query" + }, + { + "name": "refresh_false_var", + "refresh": 0, + "type": "query" + }, + { + "hide": 2, + "hideVariable": true, + "name": "hide_variable_var", + "options": [], + "type": "query" + }, + { + "hide": 1, + "hideLabel": true, + "name": "hide_label_var", + "options": [], + "type": "query" + }, + { + "hide": 2, + "hideLabel": true, + "hideVariable": true, + "name": "priority_var", + "options": [], + "type": "query" + }, + { + "name": "no_properties_var", + "options": [], + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "V12 Template Variables Migration Test", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/single_version/v13.graph_thresholds.v13.json b/apps/dashboard/pkg/migration/testdata/output/single_version/v13.graph_thresholds.v13.json new file mode 100644 index 00000000000..62545df022a --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/single_version/v13.graph_thresholds.v13.json @@ -0,0 +1,98 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "autoMigrateFrom": "graph", + "grid": { + "threshold1": 200, + "threshold1Color": "yellow", + "threshold2": 400, + "threshold2Color": "red", + "thresholdLine": true + }, + "id": 1, + "title": "Graph with Line Thresholds", + "type": "timeseries" + }, + { + "autoMigrateFrom": "graph", + "grid": { + "threshold1": 100, + "threshold1Color": "green", + "threshold2": 300, + "threshold2Color": "blue", + "thresholdLine": false + }, + "id": 2, + "title": "Graph with Fill Thresholds", + "type": "timeseries" + }, + { + "autoMigrateFrom": "graph", + "grid": { + "threshold1": 150, + "threshold1Color": "orange", + "thresholdLine": true + }, + "id": 3, + "title": "Graph with Single Threshold", + "type": "timeseries" + }, + { + "autoMigrateFrom": "graph", + "grid": { + "threshold1": 200, + "threshold1Color": "yellow", + "thresholdLine": false + }, + "id": 4, + "thresholds": [ + { + "color": "purple", + "colorMode": "custom", + "value": 50 + } + ], + "title": "Graph with Existing Thresholds", + "type": "timeseries" + }, + { + "autoMigrateFrom": "singlestat", + "id": 5, + "title": "Non-Graph Panel", + "type": "stat" + } + ], + "schemaVersion": 13, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "V13 Graph Thresholds Migration Test", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/single_version/v13.minimal_graph_config.v13.json b/apps/dashboard/pkg/migration/testdata/output/single_version/v13.minimal_graph_config.v13.json new file mode 100644 index 00000000000..ab364ee3075 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/single_version/v13.minimal_graph_config.v13.json @@ -0,0 +1,56 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "autoMigrateFrom": "graph", + "id": 4, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "gdev-prometheus" + }, + "editorMode": "builder", + "expr": "{\"a.utf8.metric 🤘\", job=\"prometheus-utf8\"}", + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "type": "timeseries" + } + ], + "schemaVersion": 13, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Dashboard with minimal graph panel settings", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/single_version/v24.table-angular.v24.json b/apps/dashboard/pkg/migration/testdata/output/single_version/v24.table-angular.v24.json index 665c477506f..6091c413889 100644 --- a/apps/dashboard/pkg/migration/testdata/output/single_version/v24.table-angular.v24.json +++ b/apps/dashboard/pkg/migration/testdata/output/single_version/v24.table-angular.v24.json @@ -21,49 +21,25 @@ "links": [], "panels": [ { + "autoMigrateFrom": "table-old", "description": "Tests basic migration with default style pattern (/.*/) containing thresholds and colors. Should convert styles to fieldConfig.defaults with threshold steps.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "red", - "value": null - }, - { - "color": "yellow", - "value": 10 - }, - { - "color": "green", - "value": 20 - }, - { - "value": 30 - } - ] - } - }, - "overrides": [] - }, "id": 1, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "legend": true, + "styles": [ + { + "colors": [ + "red", + "yellow", + "green" + ], + "pattern": "/.*/", + "thresholds": [ + "10", + "20", + "30" + ] + } + ], "targets": [ { "refId": "A" @@ -76,376 +52,151 @@ "type": "table" }, { + "autoMigrateFrom": "table-old", "description": "Tests comprehensive migration including: default style with thresholds/colors/unit/decimals/align/colorMode, column overrides with exact name and regex patterns, date formatting, hidden columns, and links with tooltips.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "center", - "cellOptions": { - "type": "color-background" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, + "id": 2, + "styles": [ + { + "align": "center", + "colorMode": "cell", + "colors": [ + "green", + "yellow", + "red" + ], "decimals": 2, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "yellow", - "value": 100 - }, - { - "color": "red", - "value": 500 - } - ] - }, + "pattern": "/.*/", + "thresholds": [ + "100", + "500" + ], "unit": "bytes" }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "Status" - }, - "properties": [ - { - "id": "displayName", - "value": "Current Status" - }, - { - "id": "unit", - "value": "short" - }, - { - "id": "decimals", - "value": 0 - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-text" - } - }, - { - "id": "custom.align", - "value": "left" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/Error.*/" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "View error details", - "url": "http://example.com/errors" - } - ] - }, - { - "id": "custom.cellOptions", - "value": { - "type": "color-background" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Time" - }, - "properties": [ - { - "id": "displayName", - "value": "Timestamp" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "Hidden" - }, - "properties": [ - { - "id": "custom.hideFrom.viz", - "value": true - } - ] - } - ] - }, - "id": 2, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + { + "alias": "Current Status", + "align": "left", + "colorMode": "value", + "decimals": 0, + "pattern": "Status", + "unit": "short" + }, + { + "colorMode": "row", + "link": true, + "linkTargetBlank": true, + "linkTooltip": "View error details", + "linkUrl": "http://example.com/errors", + "pattern": "/Error.*/" + }, + { + "alias": "Timestamp", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "pattern": "Hidden", + "type": "hidden" + } + ], "title": "Complex Table with All Style Features", "type": "table" }, { + "autoMigrateFrom": "table-old", + "columns": [ + { + "text": "Average", + "value": "avg" + }, + { + "text": "Maximum", + "value": "max" + }, + { + "text": "Minimum", + "value": "min" + }, + { + "text": "Total", + "value": "total" + }, + { + "text": "Current", + "value": "current" + }, + { + "text": "Count", + "value": "count" + } + ], "description": "Tests migration of timeseries_aggregations transform to reduce transformation with column mappings (avg-\u003emean, max-\u003emax, min-\u003emin, total-\u003esum, current-\u003elastNotNull, count-\u003ecount).", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "decimals": 1, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percent" - }, - "overrides": [] - }, "id": 3, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "decimals": 1, + "pattern": "/.*/", + "unit": "percent" + } + ], "title": "Table with Timeseries Aggregations Transform", - "transformations": [ - { - "id": "reduce", - "options": { - "includeTimeField": false, - "reducers": [ - "mean", - "max", - "min", - "sum", - "lastNotNull", - "count" - ] - } - } - ], + "transform": "timeseries_aggregations", "type": "table" }, { + "autoMigrateFrom": "table-old", "description": "Tests migration of timeseries_to_rows transform to seriesToRows transformation.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "short" - }, - "overrides": [] - }, "id": 4, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", - "title": "Table with Timeseries to Rows Transform", - "transformations": [ + "styles": [ { - "id": "seriesToRows", - "options": { - "reducers": [] - } - } - ], - "type": "table" - }, - { - "description": "Tests migration of timeseries_to_columns transform to seriesToColumns transformation.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "id": 5, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", - "title": "Table with Timeseries to Columns Transform", - "transformations": [ - { - "id": "seriesToColumns", - "options": { - "reducers": [] - } - } - ], - "type": "table" - }, - { - "description": "Tests migration of table transform to merge transformation. Also tests auto alignment conversion to empty string.", - "fieldConfig": { - "defaults": { - "custom": { - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "id": 6, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", - "title": "Table with Merge Transform", - "transformations": [ - { - "id": "merge", - "options": { - "reducers": [] - } - } - ], - "type": "table" - }, - { - "description": "Tests that existing transformations are preserved and new transformation from old format is appended to the list.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, + "pattern": "/.*/", "unit": "short" - }, - "overrides": [] - }, + } + ], + "title": "Table with Timeseries to Rows Transform", + "transform": "timeseries_to_rows", + "type": "table" + }, + { + "autoMigrateFrom": "table-old", + "description": "Tests migration of timeseries_to_columns transform to seriesToColumns transformation.", + "id": 5, + "styles": [ + { + "pattern": "/.*/", + "unit": "bytes" + } + ], + "title": "Table with Timeseries to Columns Transform", + "transform": "timeseries_to_columns", + "type": "table" + }, + { + "autoMigrateFrom": "table-old", + "description": "Tests migration of table transform to merge transformation. Also tests auto alignment conversion to empty string.", + "id": 6, + "styles": [ + { + "align": "auto", + "pattern": "/.*/" + } + ], + "title": "Table with Merge Transform", + "transform": "table", + "type": "table" + }, + { + "autoMigrateFrom": "table-old", + "description": "Tests that existing transformations are preserved and new transformation from old format is appended to the list.", "id": 7, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "pattern": "/.*/", + "unit": "short" + } + ], "title": "Table with Existing Transformations", + "transform": "timeseries_to_rows", "transformations": [ { "id": "filterFieldsByName", @@ -457,496 +208,171 @@ ] } } - }, - { - "id": "seriesToRows", - "options": { - "reducers": [] - } } ], "type": "table" }, { + "autoMigrateFrom": "table-old", "description": "Tests handling of mixed numeric and string threshold values (int, string, float) with proper type conversion.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "yellow", - "value": 10 - }, - { - "color": "orange", - "value": 20 - }, - { - "color": "red", - "value": 30.5 - } - ] - } - }, - "overrides": [] - }, "id": 8, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "colors": [ + "green", + "yellow", + "orange", + "red" + ], + "pattern": "/.*/", + "thresholds": [ + 10, + "20", + 30.5 + ] + } + ], "title": "Mixed Threshold Types", "type": "table" }, { + "autoMigrateFrom": "table-old", "description": "Tests all color mode mappings: cell-\u003ecolor-background, row-\u003ecolor-background, value-\u003ecolor-text.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "color-background" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "CellColumn" - }, - "properties": [ - { - "id": "custom.cellOptions", - "value": { - "type": "color-background" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RowColumn" - }, - "properties": [ - { - "id": "custom.cellOptions", - "value": { - "type": "color-background" - } - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "ValueColumn" - }, - "properties": [ - { - "id": "custom.cellOptions", - "value": { - "type": "color-text" - } - } - ] - } - ] - }, "id": 9, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "colorMode": "cell", + "pattern": "/.*/" + }, + { + "colorMode": "cell", + "pattern": "CellColumn" + }, + { + "colorMode": "row", + "pattern": "RowColumn" + }, + { + "colorMode": "value", + "pattern": "ValueColumn" + } + ], "title": "All Color Modes Test", "type": "table" }, { + "autoMigrateFrom": "table-old", "description": "Tests all alignment options: left, center, right, and auto (should convert to empty string).", - "fieldConfig": { - "defaults": { - "custom": { - "align": "center", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "LeftColumn" - }, - "properties": [ - { - "id": "custom.align", - "value": "left" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "RightColumn" - }, - "properties": [ - { - "id": "custom.align", - "value": "right" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "AutoColumn" - }, - "properties": [ - { - "id": "custom.align" - } - ] - } - ] - }, "id": 10, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + "styles": [ + { + "align": "center", + "pattern": "/.*/" + }, + { + "align": "left", + "pattern": "LeftColumn" + }, + { + "align": "right", + "pattern": "RightColumn" + }, + { + "align": "auto", + "pattern": "AutoColumn" + } + ], "title": "All Alignment Options", "type": "table" }, { + "autoMigrateFrom": "table-old", "description": "Tests both field matcher types: byName for exact matches and byRegexp for regex patterns.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, + "id": 11, + "styles": [ + { + "pattern": "/.*/", "unit": "short" }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "ExactColumnName" - }, - "properties": [ - { - "id": "displayName", - "value": "Exact Match" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/Regex.*Pattern/" - }, - "properties": [ - { - "id": "displayName", - "value": "Regex Match" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/^Start/" - }, - "properties": [ - { - "id": "displayName", - "value": "Start Pattern" - } - ] - }, - { - "matcher": { - "id": "byRegexp", - "options": "/End$/" - }, - "properties": [ - { - "id": "displayName", - "value": "End Pattern" - } - ] - } - ] - }, - "id": 11, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + { + "alias": "Exact Match", + "pattern": "ExactColumnName" + }, + { + "alias": "Regex Match", + "pattern": "/Regex.*Pattern/" + }, + { + "alias": "Start Pattern", + "pattern": "/^Start/" + }, + { + "alias": "End Pattern", + "pattern": "/End$/" + } + ], "title": "Field Matcher Types Test", "type": "table" }, { + "autoMigrateFrom": "table-old", "description": "Tests various link configurations: with and without tooltip, with and without target blank.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, + "id": 12, + "styles": [ + { + "pattern": "/.*/", "unit": "short" }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "LinkWithTooltip" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": true, - "title": "Click to view details", - "url": "http://example.com/with-tooltip" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "LinkWithoutTooltip" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": false, - "title": "", - "url": "http://example.com/no-tooltip" - } - ] - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "LinkMinimal" - }, - "properties": [ - { - "id": "links", - "value": [ - { - "targetBlank": false, - "title": "", - "url": "http://example.com/minimal" - } - ] - } - ] - } - ] - }, - "id": 12, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + { + "link": true, + "linkTargetBlank": true, + "linkTooltip": "Click to view details", + "linkUrl": "http://example.com/with-tooltip", + "pattern": "LinkWithTooltip" + }, + { + "link": true, + "linkTargetBlank": false, + "linkUrl": "http://example.com/no-tooltip", + "pattern": "LinkWithoutTooltip" + }, + { + "link": true, + "linkUrl": "http://example.com/minimal", + "pattern": "LinkMinimal" + } + ], "title": "Link Configuration Test", "type": "table" }, { + "autoMigrateFrom": "table-old", "description": "Tests various date format patterns and aliases.", - "fieldConfig": { - "defaults": { - "custom": { - "align": "auto", - "cellOptions": { - "type": "auto" - }, - "footer": { - "reducers": [] - }, - "inspect": false - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, + "id": 13, + "styles": [ + { + "pattern": "/.*/", "unit": "short" }, - "overrides": [ - { - "matcher": { - "id": "byName", - "options": "DateISO" - }, - "properties": [ - { - "id": "displayName", - "value": "ISO Date" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "DateTime" - }, - "properties": [ - { - "id": "displayName", - "value": "Full DateTime" - }, - { - "id": "unit", - "value": "time: YYYY-MM-DD HH:mm:ss" - } - ] - }, - { - "matcher": { - "id": "byName", - "options": "TimeOnly" - }, - "properties": [ - { - "id": "displayName", - "value": "Time Only" - }, - { - "id": "unit", - "value": "time: HH:mm:ss" - } - ] - } - ] - }, - "id": 13, - "options": { - "cellHeight": "sm", - "showHeader": true - }, - "pluginVersion": "12.1.0", + { + "alias": "ISO Date", + "dateFormat": "YYYY-MM-DD", + "pattern": "DateISO", + "type": "date" + }, + { + "alias": "Full DateTime", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "DateTime", + "type": "date" + }, + { + "alias": "Time Only", + "dateFormat": "HH:mm:ss", + "pattern": "TimeOnly", + "type": "date" + } + ], "title": "Date Format Variations", "type": "table" }, diff --git a/apps/dashboard/pkg/migration/testdata/output/single_version/v28.singlestat_and_variable_properties.v28.json b/apps/dashboard/pkg/migration/testdata/output/single_version/v28.singlestat_and_variable_properties.v28.json index ba43b6785d3..2b7b8e83aa3 100644 --- a/apps/dashboard/pkg/migration/testdata/output/single_version/v28.singlestat_and_variable_properties.v28.json +++ b/apps/dashboard/pkg/migration/testdata/output/single_version/v28.singlestat_and_variable_properties.v28.json @@ -21,48 +21,18 @@ "links": [], "panels": [ { - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], + "grid": { + "max": 10, + "min": 1 }, "id": 1, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "legend": true, "targets": [ { "refId": "A" @@ -71,122 +41,48 @@ "refId": "B" } ], + "thresholds": "10,20,30", "type": "stat" }, { - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], + "gauge": { + "show": true, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "grid": { + "max": 10, + "min": 1 }, "id": 2, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "thresholds": "10,20,30", "type": "stat" }, { - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "20": { - "text": "test" - } - }, - "type": "value" - }, - { - "options": { - "30": { - "text": "test1" - } - }, - "type": "value" - }, - { - "options": { - "40": { - "color": "orange", - "text": "50" - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], + "grid": { + "max": 10, + "min": 1 }, "id": 3, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "legend": true, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + } + ], "targets": [ { "refId": "A" @@ -195,7 +91,25 @@ "refId": "B" } ], - "type": "stat" + "thresholds": "10,20,30", + "type": "stat", + "valueMaps": [ + { + "op": "=", + "text": "test", + "value": "20" + }, + { + "op": "=", + "text": "test1", + "value": "30" + }, + { + "op": "=", + "text": "50", + "value": "40" + } + ] }, { "id": 4, diff --git a/apps/dashboard/pkg/migration/testdata/output/single_version/v28.singlestat_migration.v28.json b/apps/dashboard/pkg/migration/testdata/output/single_version/v28.singlestat_migration.v28.json index 55a93c8a0f1..77f8ce45a63 100644 --- a/apps/dashboard/pkg/migration/testdata/output/single_version/v28.singlestat_migration.v28.json +++ b/apps/dashboard/pkg/migration/testdata/output/single_version/v28.singlestat_migration.v28.json @@ -21,48 +21,18 @@ "links": [], "panels": [ { - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], + "grid": { + "max": 10, + "min": 1 }, "id": 1, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "legend": true, "targets": [ { "refId": "A" @@ -71,47 +41,22 @@ "refId": "B" } ], + "thresholds": "10,20,30", "type": "stat" }, { - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], + "grid": { + "max": 10, + "min": 1 }, "id": 2, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "legend": true, "targets": [ { "refId": "A" @@ -120,122 +65,48 @@ "refId": "B" } ], + "thresholds": "", "type": "stat" }, { - "fieldConfig": { - "defaults": { - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], + "gauge": { + "show": true, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "grid": { + "max": 10, + "min": 1 }, "id": 3, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "thresholds": "10,20,30", "type": "stat" }, { - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "20": { - "text": "test" - } - }, - "type": "value" - }, - { - "options": { - "30": { - "text": "test1" - } - }, - "type": "value" - }, - { - "options": { - "40": { - "color": "orange", - "text": "50" - } - }, - "type": "value" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "#FF0000", - "value": null - }, - { - "color": "green", - "value": 10 - }, - { - "color": "orange", - "value": 20 - } - ] - } - }, - "overrides": [] + "autoMigrateFrom": "singlestat", + "colors": [ + "#FF0000", + "green", + "orange" + ], + "grid": { + "max": 10, + "min": 1 }, "id": 4, - "options": { - "colorMode": "none", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "12.1.0", + "legend": true, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + } + ], "targets": [ { "refId": "A" @@ -244,7 +115,25 @@ "refId": "B" } ], - "type": "stat" + "thresholds": "10,20,30", + "type": "stat", + "valueMaps": [ + { + "op": "=", + "text": "test", + "value": "20" + }, + { + "op": "=", + "text": "test1", + "value": "30" + }, + { + "op": "=", + "text": "50", + "value": "40" + } + ] }, { "id": 8, @@ -257,38 +146,24 @@ "type": "timeseries" }, { + "autoMigrateFrom": "grafana-singlestat-panel", + "colorBackground": false, + "colorValue": true, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], "datasource": { "type": "prometheus" }, - "fieldConfig": { - "defaults": { - "mappings": [ - { - "options": { - "match": "null", - "result": { - "text": "N/A" - } - }, - "type": "special" - } - ], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "areaF2" - }, - "overrides": [] + "format": "areaF2", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true }, "gridPos": { "h": 8, @@ -297,25 +172,37 @@ "y": 43 }, "id": 5, - "maxDataPoints": 100, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "horizontal", - "percentChangeColorMode": "standard", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "postfix": "b", + "postfixFontSize": "50%", + "prefix": "a", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true }, - "pluginVersion": "12.1.0", + "tableColumn": "", "targets": [ { "datasource": { @@ -325,8 +212,18 @@ "refId": "A" } ], + "thresholds": "", "title": "grafana-singlestat-panel", - "type": "stat" + "type": "stat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "avg" }, { "datasource": { diff --git a/apps/dashboard/pkg/migration/testdata/output/single_version/v7.timepicker.v7.json b/apps/dashboard/pkg/migration/testdata/output/single_version/v7.timepicker.v7.json new file mode 100644 index 00000000000..2f0c571d3b4 --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/single_version/v7.timepicker.v7.json @@ -0,0 +1,80 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "id": 1, + "targets": [ + { + "expr": "up", + "refId": "A" + }, + { + "expr": "cpu_usage", + "refId": "B" + } + ] + } + ], + "schemaVersion": 7, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "collapse": false, + "enable": true, + "notice": false, + "now": true, + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "status": "Stable", + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ], + "type": "timepicker" + }, + "timezone": "", + "title": "No Title", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/single_version/v8.influxdb_query.v8.json b/apps/dashboard/pkg/migration/testdata/output/single_version/v8.influxdb_query.v8.json new file mode 100644 index 00000000000..a098d59850c --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/single_version/v8.influxdb_query.v8.json @@ -0,0 +1,122 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "autoMigrateFrom": "graph", + "id": 1, + "targets": [ + { + "groupBy": [ + { + "params": [ + "1m" + ], + "type": "time" + }, + { + "params": [ + "host" + ], + "type": "tag" + }, + { + "params": [ + "null" + ], + "type": "fill" + } + ], + "refId": "A", + "select": [ + [ + { + "params": [ + "value" + ], + "type": "field" + }, + { + "params": [], + "type": "mean" + }, + { + "params": [ + "*2" + ], + "type": "math" + }, + { + "params": [ + "doubled" + ], + "type": "alias" + } + ] + ], + "tags": [ + { + "key": "host", + "value": "server1" + } + ] + } + ], + "type": "timeseries" + }, + { + "autoMigrateFrom": "graph", + "id": 2, + "targets": [ + { + "groupBy": [ + { + "interval": "1m", + "type": "time" + } + ], + "rawQuery": true, + "refId": "A", + "tags": [ + { + "key": "host", + "value": "server1" + } + ] + } + ], + "type": "timeseries" + } + ], + "schemaVersion": 8, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "V8 InfluxDB Query Test", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/dashboard/pkg/migration/testdata/output/single_version/v9.no-op.v9.json b/apps/dashboard/pkg/migration/testdata/output/single_version/v9.no-op.v9.json new file mode 100644 index 00000000000..cb73f4b25aa --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/output/single_version/v9.no-op.v9.json @@ -0,0 +1,52 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "autoMigrateFrom": "singlestat", + "id": 1, + "type": "stat" + }, + { + "autoMigrateFrom": "singlestat", + "id": 2, + "type": "stat" + }, + { + "autoMigrateFrom": "graph", + "id": 3, + "type": "timeseries" + } + ], + "schemaVersion": 9, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "V9 No-Op Migration Test", + "weekStart": "" +} \ No newline at end of file diff --git a/apps/folder/go.mod b/apps/folder/go.mod index 6db44655eeb..daf5783d343 100644 --- a/apps/folder/go.mod +++ b/apps/folder/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/folder -go 1.24.6 +go 1.25.3 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -49,7 +49,7 @@ require ( go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect diff --git a/apps/folder/go.sum b/apps/folder/go.sum index ab3cbcecbb1..68909c968ec 100644 --- a/apps/folder/go.sum +++ b/apps/folder/go.sum @@ -124,8 +124,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/iam/go.mod b/apps/iam/go.mod index 30fa2ce8562..7e77d0c830b 100644 --- a/apps/iam/go.mod +++ b/apps/iam/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/iam -go 1.24.6 +go 1.25.3 replace github.com/grafana/grafana => ../../ @@ -173,7 +173,6 @@ require ( github.com/go-openapi/strfmt v0.23.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-openapi/validate v0.24.0 // indirect - github.com/go-redis/redis/v8 v8.11.5 // indirect github.com/go-sql-driver/mysql v1.9.3 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect @@ -327,6 +326,7 @@ require ( github.com/prometheus/procfs v0.16.1 // indirect github.com/prometheus/prometheus v0.303.1 // indirect github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect + github.com/redis/go-redis/v9 v9.14.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rs/cors v1.11.1 // indirect @@ -405,7 +405,7 @@ require ( golang.org/x/crypto v0.42.0 // indirect golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/iam/go.sum b/apps/iam/go.sum index 8761accef79..aa2d47b9a2e 100644 --- a/apps/iam/go.sum +++ b/apps/iam/go.sum @@ -310,6 +310,10 @@ github.com/blugelabs/ice/v2 v2.0.1/go.mod h1:QxAWSPNwZwsIqS25c3lbIPFQrVvT1sphf5x github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous= github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= @@ -543,8 +547,6 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= 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-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= @@ -1051,8 +1053,6 @@ github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJm github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nikunjy/rules v1.5.0 h1:KJDSLOsFhwt7kcXUyZqwkgrQg5YoUwj+TVu6ItCQShw= github.com/nikunjy/rules v1.5.0/go.mod h1:TlZtZdBChrkqi8Lr2AXocme8Z7EsbxtFdDoKeI6neBQ= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 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= @@ -1066,9 +1066,8 @@ github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNs github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -1181,8 +1180,8 @@ github.com/protocolbuffers/txtpbfmt v0.0.0-20241112170944-20d2c9ebc01d/go.mod h1 github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= -github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/redis/go-redis/v9 v9.14.0 h1:u4tNCjXOyzfgeLN+vAZaW1xUooqWDqVEsZN0U01jfAE= +github.com/redis/go-redis/v9 v9.14.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -1567,8 +1566,8 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2023,7 +2022,6 @@ gopkg.in/src-d/go-errors.v1 v1.0.0 h1:cooGdZnCjYbeS1zb1s6pVAAimTdKceRrpn7aKOnNIf gopkg.in/src-d/go-errors.v1 v1.0.0/go.mod h1:q1cBlomlw2FnDBDNGlnh6X0jPihy+QxZfMMNxPCbdYg= gopkg.in/telebot.v3 v3.3.8 h1:uVDGjak9l824FN9YARWUHMsiNZnlohAVwUycw21k6t8= gopkg.in/telebot.v3 v3.3.8/go.mod h1:1mlbqcLTVSfK9dx7fdp+Nb5HZsy4LLPtpZTKmwhwtzM= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/apps/iam/local/Dockerfile b/apps/iam/local/Dockerfile index 28df71c2384..4777b0a466d 100644 --- a/apps/iam/local/Dockerfile +++ b/apps/iam/local/Dockerfile @@ -1,5 +1,5 @@ # Build stage -FROM golang:1.24.6-alpine AS builder +FROM golang:1.25.3-alpine AS builder # Set working directory WORKDIR /app diff --git a/apps/investigations/go.mod b/apps/investigations/go.mod index 1652941eba8..2e2704b8e67 100644 --- a/apps/investigations/go.mod +++ b/apps/investigations/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/investigations -go 1.24.6 +go 1.25.3 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -67,7 +67,7 @@ require ( go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/investigations/go.sum b/apps/investigations/go.sum index dadd72070fd..ff48e3193e0 100644 --- a/apps/investigations/go.sum +++ b/apps/investigations/go.sum @@ -161,8 +161,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/playlist/go.mod b/apps/playlist/go.mod index 4419785540a..02cf3943f6f 100644 --- a/apps/playlist/go.mod +++ b/apps/playlist/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/playlist -go 1.24.6 +go 1.25.3 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -68,7 +68,7 @@ require ( go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/playlist/go.sum b/apps/playlist/go.sum index dadd72070fd..ff48e3193e0 100644 --- a/apps/playlist/go.sum +++ b/apps/playlist/go.sum @@ -161,8 +161,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/plugins/go.mod b/apps/plugins/go.mod index 35ed57d995b..7c803ef1e15 100644 --- a/apps/plugins/go.mod +++ b/apps/plugins/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/plugins -go 1.24.4 +go 1.25.3 require ( github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37 @@ -75,7 +75,7 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.42.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/plugins/go.sum b/apps/plugins/go.sum index ca929862af5..16411aeecb3 100644 --- a/apps/plugins/go.sum +++ b/apps/plugins/go.sum @@ -177,8 +177,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/preferences/go.mod b/apps/preferences/go.mod index 3c4b1af454b..932af3adf61 100644 --- a/apps/preferences/go.mod +++ b/apps/preferences/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/preferences -go 1.24.6 +go 1.25.3 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -49,7 +49,7 @@ require ( go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect diff --git a/apps/preferences/go.sum b/apps/preferences/go.sum index 112d7cb18ab..0bca000ff09 100644 --- a/apps/preferences/go.sum +++ b/apps/preferences/go.sum @@ -124,8 +124,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/provisioning/go.mod b/apps/provisioning/go.mod index 9a3cd99c066..e30a0b77cc7 100644 --- a/apps/provisioning/go.mod +++ b/apps/provisioning/go.mod @@ -1,8 +1,9 @@ module github.com/grafana/grafana/apps/provisioning -go 1.24.6 +go 1.25.3 require ( + github.com/fsnotify/fsnotify v1.9.0 github.com/google/go-github/v70 v70.0.0 github.com/google/uuid v1.6.0 github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f @@ -67,7 +68,7 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.42.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect diff --git a/apps/provisioning/go.sum b/apps/provisioning/go.sum index 93819002c42..ce659fcf611 100644 --- a/apps/provisioning/go.sum +++ b/apps/provisioning/go.sum @@ -10,6 +10,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 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/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY= @@ -166,8 +168,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/provisioning/pkg/repository/github/webhook.go b/apps/provisioning/pkg/repository/github/webhook.go index 40f87ddc93d..0da826fe9de 100644 --- a/apps/provisioning/pkg/repository/github/webhook.go +++ b/apps/provisioning/pkg/repository/github/webhook.go @@ -7,7 +7,6 @@ import ( "log/slog" "net/http" "slices" - "strings" "github.com/google/go-github/v70/github" "github.com/google/uuid" @@ -16,7 +15,6 @@ import ( "github.com/grafana/grafana-app-sdk/logging" provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/apps/provisioning/pkg/repository" - "github.com/grafana/grafana/apps/provisioning/pkg/safepath" common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" ) @@ -125,27 +123,13 @@ func (r *githubWebhookRepository) parsePushEvent(event *github.PushEvent) (*prov // however, if we get an event where just a .keep file is being deleted, and no other files in the folder // are being deleted, the folder could be gone from git, but not from grafana and we do not have a way // to get the grafana uid to delete the folder. so, instead, we will queue a full sync to clean things up. - dirsWithKeepDeletes := make(map[string]struct{}) - dirsWithOtherDeletes := make(map[string]struct{}) + var deletedPaths []string for _, change := range event.GetCommits() { - for _, removedFile := range change.Removed { - dir := safepath.Dir(removedFile) - if strings.HasSuffix(removedFile, ".keep") { - dirsWithKeepDeletes[dir] = struct{}{} - } else { - dirsWithOtherDeletes[dir] = struct{}{} - } - } - } - // if there are any keep files deleted that do not have other files deleted in the same folder, we need to queue a full sync - incremental := true - for dir := range dirsWithKeepDeletes { - if _, exists := dirsWithOtherDeletes[dir]; !exists { - incremental = false - break - } + deletedPaths = append(deletedPaths, change.Removed...) } + incremental := repository.CanUseIncrementalSync(deletedPaths) + return &provisioning.WebhookResponse{ Code: http.StatusAccepted, Job: &provisioning.JobSpec{ diff --git a/apps/provisioning/pkg/repository/local/watch.go b/apps/provisioning/pkg/repository/local/watch.go new file mode 100644 index 00000000000..91caed9a1d3 --- /dev/null +++ b/apps/provisioning/pkg/repository/local/watch.go @@ -0,0 +1,130 @@ +package local + +import ( + "context" + "fmt" + "io/fs" + "math" + "os" + "path/filepath" + "strings" + "sync" + "time" + + "github.com/fsnotify/fsnotify" + + "github.com/grafana/grafana-app-sdk/logging" +) + +type FileWatcher interface { + Watch(ctx context.Context, events chan<- string) +} + +type fileWatcher struct { + prefix string + accept func(string) bool + waitFor time.Duration + timersMu sync.Mutex + timers map[string]*time.Timer + watcher *fsnotify.Watcher + logger logging.Logger +} + +// File watcher that buffers events for 100ms before actually firing them +// this is helpful because editing a file may often update the same file many many times +// for what seems like a single operation. +// See: https://github.com/fsnotify/fsnotify/blob/main/cmd/fsnotify/dedup.go +func NewFileWatcher(path string, accept func(string) bool) (FileWatcher, error) { + info, _ := os.Stat(path) + if info == nil || !info.IsDir() { + return nil, fmt.Errorf("expecting to watch a folder") + } + + w, err := fsnotify.NewWatcher() + if err != nil { + return nil, err + } + + if err := w.Add(path); err != nil { + _ = w.Close() + return nil, err + } + + if err = filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + if err = w.Add(path); err != nil { + return err + } + } + return nil + }); err != nil { + _ = w.Close() + return nil, err + } + + return &fileWatcher{ + prefix: path + "/", + accept: accept, + waitFor: 100 * time.Millisecond, + timers: make(map[string]*time.Timer), + watcher: w, + logger: logging.DefaultLogger.With("watch", path), + }, nil +} + +// Keep watching for changes until the context is done +func (f *fileWatcher) Watch(ctx context.Context, events chan<- string) { + for { + select { + case <-ctx.Done(): + close(events) + return + + case _, ok := <-f.watcher.Errors: + if !ok { // Channel was closed (i.e. Watcher.Close() was called). + close(events) + return + } + + // Read from Events. + case e, ok := <-f.watcher.Events: + if !ok { // Channel was closed (i.e. Watcher.Close() was called). + close(events) + return + } + name := filepath.Base(e.Name) + if strings.HasPrefix(name, ".") { + continue // ignore hidden files+folders + } + if !f.accept(name) { + info, _ := os.Stat(e.Name) + if info != nil && info.IsDir() { + if err := f.watcher.Add(e.Name); err != nil { + f.logger.Warn("error adding folder", "folder", e.Name, "error", err) + } + } + continue + } + + f.timersMu.Lock() + t, ok := f.timers[e.Name] + if !ok { + nameCopy := e.Name + t = time.AfterFunc(math.MaxInt64, func() { + path, _ := strings.CutPrefix(nameCopy, f.prefix) + events <- path + + f.timersMu.Lock() + delete(f.timers, nameCopy) + f.timersMu.Unlock() + }) + f.timers[e.Name] = t + } + f.timersMu.Unlock() + t.Reset(f.waitFor) + } + } +} diff --git a/apps/provisioning/pkg/repository/local/watch_test.go b/apps/provisioning/pkg/repository/local/watch_test.go new file mode 100644 index 00000000000..ef271d345a9 --- /dev/null +++ b/apps/provisioning/pkg/repository/local/watch_test.go @@ -0,0 +1,84 @@ +package local + +import ( + "context" + "os" + "path/filepath" + "slices" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestWatch_TempFiles(t *testing.T) { + tmpdir := t.TempDir() + sub1 := filepath.Join(tmpdir, "sub1") + err := os.MkdirAll(sub1, 0700) + require.NoError(t, err) + + watcher, err := NewFileWatcher(tmpdir, func(name string) bool { + return filepath.Ext(name) == ".txt" + }) + require.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + events := make(chan string, 10) + go func() { + watcher.Watch(ctx, events) + }() + + go func() { + time.Sleep(20 * time.Millisecond) + err = os.WriteFile(filepath.Join(tmpdir, "aaa.txt"), []byte("aaa"), 0600) + require.NoError(t, err) + err = os.WriteFile(filepath.Join(tmpdir, ".hidden.txt"), []byte("hidden"), 0600) + require.NoError(t, err) + err = os.WriteFile(filepath.Join(tmpdir, "bbb.txt"), []byte("bbb"), 0600) + require.NoError(t, err) + err = os.WriteFile(filepath.Join(tmpdir, "xxx.json"), []byte("ignore"), 0600) + require.NoError(t, err) + err = os.WriteFile(filepath.Join(sub1, "ccc.txt"), []byte("ccc"), 0600) + require.NoError(t, err) + + // make a sub folder + sub2 := filepath.Join(tmpdir, "sub2") + err = os.MkdirAll(sub2, 0700) + require.NoError(t, err) + time.Sleep(50 * time.Millisecond) + err = os.WriteFile(filepath.Join(sub2, "ddd.txt"), []byte("ddd"), 0600) + require.NoError(t, err) + + // Check all the paths we are watching + w, ok := watcher.(*fileWatcher) + require.True(t, ok, "explicit cast") + watching := w.watcher.WatchList() + slices.Sort(watching) + require.Equal(t, []string{ + tmpdir, sub1, sub2, + }, watching) + + // Removing the subfolder should trigger the event again + time.Sleep(time.Millisecond * 150) + err = os.RemoveAll(sub2) + require.NoError(t, err) + + // Finish all the events + time.Sleep(time.Millisecond * 250) + cancel() // stops the context + }() + + received := []string{} + for event := range events { + received = append(received, event) + } + slices.Sort(received) + + require.Equal(t, []string{ + "aaa.txt", + "bbb.txt", + "sub1/ccc.txt", + "sub2/ddd.txt", // first time because we added it + "sub2/ddd.txt", // second time because we removed it + }, received) +} diff --git a/apps/provisioning/pkg/repository/workflows.go b/apps/provisioning/pkg/repository/workflows.go index 48ba1319a62..5657de9dc5e 100644 --- a/apps/provisioning/pkg/repository/workflows.go +++ b/apps/provisioning/pkg/repository/workflows.go @@ -1,9 +1,12 @@ package repository import ( + "strings" + apierrors "k8s.io/apimachinery/pkg/api/errors" provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" + "github.com/grafana/grafana/apps/provisioning/pkg/safepath" ) func IsWriteAllowed(repo *provisioning.Repository, ref string) error { @@ -40,3 +43,32 @@ func IsWriteAllowed(repo *provisioning.Repository, ref string) error { return nil } } + +// CanUseIncrementalSync checks if an incremental sync can be performed or if a full sync is needed, +// given a list of deleted file paths. It will return true if a .keep file is deleted without +// other files being deleted in the same directory. This is because the folder will not be a part of the +// deleted files, and the .keep file is not a resource in grafana, so we can't get the folder uid. +// A full sync will clean that up. +func CanUseIncrementalSync(deletedPaths []string) bool { + dirsWithKeepDeletes := make(map[string]struct{}) + dirsWithOtherDeletes := make(map[string]struct{}) + + for _, path := range deletedPaths { + dir := safepath.Dir(path) + if strings.HasSuffix(path, ".keep") { + dirsWithKeepDeletes[dir] = struct{}{} + } else { + dirsWithOtherDeletes[dir] = struct{}{} + } + } + + // if there are any .keep files deleted that don't have other files deleted in the same folder, + // we need to do a full sync + for dir := range dirsWithKeepDeletes { + if _, exists := dirsWithOtherDeletes[dir]; !exists { + return false + } + } + + return true +} diff --git a/apps/provisioning/pkg/repository/workflows_test.go b/apps/provisioning/pkg/repository/workflows_test.go index 995c85e0eb5..91a69daf35a 100644 --- a/apps/provisioning/pkg/repository/workflows_test.go +++ b/apps/provisioning/pkg/repository/workflows_test.go @@ -345,3 +345,58 @@ func TestIsWriteAllowed(t *testing.T) { }) } } + +func TestCanUseIncrementalSync(t *testing.T) { + tests := []struct { + name string + deletedPaths []string + want bool + }{ + { + name: "no deleted paths", + deletedPaths: []string{}, + want: true, + }, + { + name: "no keep file deletions", + deletedPaths: []string{"test.json"}, + want: true, + }, + { + name: "keep file deletion at root without other deletions", + deletedPaths: []string{".keep"}, + want: false, + }, + { + name: "keep file deletion with other deletions in same folder", + deletedPaths: []string{"test/.keep", "test/test.json"}, + want: true, + }, + { + name: "multiple keep files in different folders without other deletions", + deletedPaths: []string{"folder1/.keep", "folder2/.keep"}, + want: false, + }, + { + name: "nested folder with only keep file deleted", + deletedPaths: []string{"parent/child/.keep"}, + want: false, + }, + { + name: "some folders with only keep, some with other files", + deletedPaths: []string{"folder1/.keep", "folder2/.keep", "folder2/dashboard.json"}, + want: false, + }, + { + name: "only regular files deleted from multiple folders", + deletedPaths: []string{"folder1/file1.json", "folder2/file2.json", "folder3/file3.json"}, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := CanUseIncrementalSync(tt.deletedPaths) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/apps/scope/go.mod b/apps/scope/go.mod index 435e401adba..a6ed7ba83a1 100644 --- a/apps/scope/go.mod +++ b/apps/scope/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/scope -go 1.24.6 +go 1.25.3 require ( github.com/grafana/grafana/pkg/apimachinery v0.0.0-20251007081214-26e147d01f0a @@ -29,7 +29,7 @@ require ( github.com/x448/float16 v0.8.4 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/text v0.29.0 // indirect google.golang.org/protobuf v1.36.9 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/apps/scope/go.sum b/apps/scope/go.sum index 25bae6f532a..7bf603654c8 100644 --- a/apps/scope/go.sum +++ b/apps/scope/go.sum @@ -71,8 +71,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= 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= diff --git a/apps/secret/go.mod b/apps/secret/go.mod index 1a6b9ecc824..0ff5e5f02eb 100644 --- a/apps/secret/go.mod +++ b/apps/secret/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/secret -go 1.24.6 +go 1.25.3 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -54,7 +54,7 @@ require ( go.opentelemetry.io/otel/trace v1.38.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/term v0.35.0 // indirect diff --git a/apps/secret/go.sum b/apps/secret/go.sum index 712a7a12807..54152dfd80d 100644 --- a/apps/secret/go.sum +++ b/apps/secret/go.sum @@ -136,8 +136,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/apps/shorturl/go.mod b/apps/shorturl/go.mod index 7eb91409ca0..fe039294882 100644 --- a/apps/shorturl/go.mod +++ b/apps/shorturl/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana/apps/shorturl -go 1.24.6 +go 1.25.3 require ( github.com/grafana/grafana-app-sdk v0.46.0 @@ -73,7 +73,7 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.42.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/net v0.45.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.36.0 // indirect diff --git a/apps/shorturl/go.sum b/apps/shorturl/go.sum index 06c31439b6e..5b7d01fbcd9 100644 --- a/apps/shorturl/go.sum +++ b/apps/shorturl/go.sum @@ -175,8 +175,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/conf/defaults.ini b/conf/defaults.ini index 1c47f30e6f3..b057f887fb4 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -1194,11 +1194,39 @@ facility = tag = [log.frontend] -# Should Faro javascript agent be initialized +# Enables the Grafana Faro javascript agent integration for capturing frontend performance and error events. enabled = false -# Custom HTTP endpoint to send events to. Default will log the events to stdout. -custom_endpoint = +# Custom HTTP endpoint to send Grafana Faro events to. Default will send events to /log-grafana-javascript-agent and log the events to stdout. +custom_endpoint = /log-grafana-javascript-agent + +# API Key for Grafana Faro custom endpoint +api_key = + +# level of internal logging for debugging Grafana Javascript Agent. +# possible values are: 0 = OFF, 1 = ERROR, 2 = WARN, 3 = INFO, 4 = VERBOSE +# more details: https://github.com/grafana/faro-web-sdk/blob/v1.3.7/docs/sources/tutorials/quick-start-browser.md#how-to-activate-debugging +internal_logger_level = 0 + +# Enables the Console instrumentation for Grafana Faro +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/console-instrumentation/ +instrumentations_console_enabled = true + +# Enables the Performance instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/performance-instrumentation/ +instrumentations_performance_enabled = true + +# Enables the Content Security Policy Violations instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/csp-violation-tracking/ +instrumentations_csp_enabled = true + +# Enables the Tracing instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/tracing-instrumentation/ +instrumentations_tracing_enabled = true + +# Enables sending attribution data for web vitals with the Performance instrumentation. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/web-vitals/#web-vitals-attribution-data +web_vitals_attribution_enabled = true # Requests per second limit enforced per an extended period, for Grafana backend log ingestion endpoint (/log). log_endpoint_requests_per_second_limit = 3 @@ -1206,28 +1234,6 @@ log_endpoint_requests_per_second_limit = 3 # Max requests accepted per short interval of time for Grafana backend log ingestion endpoint (/log) log_endpoint_burst_limit = 15 -# Enables all Faro default instrumentation by using `getWebInstrumentations`. Overrides other instrumentation flags. -instrumentations_all_enabled = false - -# Should error instrumentation be enabled, only affects Grafana Javascript Agent -instrumentations_errors_enabled = true - -# Should console instrumentation be enabled, only affects Grafana Javascript Agent -instrumentations_console_enabled = false - -# Should webvitals instrumentation be enabled, only affects Grafana Javascript Agent -instrumentations_webvitals_enabled = false - -# Should tracing instrumentation be enabled, only affects Grafana Javascript Agent -instrumentations_tracing_enabled = false - -# level of internal logging for debugging Grafana Javascript Agent. -# possible values are: 0 = OFF, 1 = ERROR, 2 = WARN, 3 = INFO, 4 = VERBOSE -# more details: https://github.com/grafana/faro-web-sdk/blob/v1.3.7/docs/sources/tutorials/quick-start-browser.md#how-to-activate-debugging -internal_logger_level = 0 - -# Api Key, only applies to Grafana Javascript Agent provider -api_key = #################################### Usage Quotas ######################## [quota] diff --git a/conf/sample.ini b/conf/sample.ini index 964b3df5196..eb59e6688f3 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -1172,35 +1172,39 @@ ;tag = [log.frontend] -# Should Faro javascript agent be initialized +# Enables the Grafana Faro javascript agent integration for capturing frontend performance and error events. ;enabled = false -# Custom HTTP endpoint to send events to. Default will log the events to stdout. +# Custom HTTP endpoint to send Grafana Faro events to. Default will send events to /log-grafana-javascript-agent and log the events to stdout. ;custom_endpoint = /log-grafana-javascript-agent -# Requests per second limit enforced an extended period, for Grafana backend log ingestion endpoint (/log). -;log_endpoint_requests_per_second_limit = 3 +# API Key for Grafana Faro custom endpoint +;api_key = -# Max requests accepted per short interval of time for Grafana backend log ingestion endpoint (/log). -;log_endpoint_burst_limit = 15 +# level of internal logging for debugging Grafana Javascript Agent. +# possible values are: 0 = OFF, 1 = ERROR, 2 = WARN, 3 = INFO, 4 = VERBOSE +# more details: https://github.com/grafana/faro-web-sdk/blob/v1.3.7/docs/sources/tutorials/quick-start-browser.md#how-to-activate-debugging +;internal_logger_level = -# Enables all Faro default instrumentation by using `getWebInstrumentations`. Overrides other instrumentation flags. -;instrumentations_all_enabled = false +# Enables the Console instrumentation for Grafana Faro +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/console-instrumentation/ +;instrumentations_console_enabled = true -# Should error instrumentation be enabled, only affects Grafana Javascript Agent -;instrumentations_errors_enabled = true +# Enables the Performance instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/performance-instrumentation/ +;instrumentations_performance_enabled = true -# Should console instrumentation be enabled, only affects Grafana Javascript Agent -;instrumentations_console_enabled = false +# Enables the Content Security Policy Violations instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/csp-violation-tracking/ +; instrumentations_csp_enabled = true -# Should webvitals instrumentation be enabled, only affects Grafana Javascript Agent -;instrumentations_webvitals_enabled = false +# Enables the Tracing instrumentation for Grafana Faro. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/tracing-instrumentation/ +;instrumentations_tracing_enabled = true -# Should tracing instrumentation be enabled, only affects Grafana Javascript Agent -;instrumentations_tracing_enabled = false - -# Api Key, only applies to Grafana Javascript Agent provider -;api_key = testApiKey +# Enables sending attribution data for web vitals with the Performance instrumentation. +# See https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/web-vitals/#web-vitals-attribution-data +;web_vitals_attribution_enabled = true #################################### Usage Quotas ######################## [quota] diff --git a/devenv/docker/blocks/prometheus_high_card/go.mod b/devenv/docker/blocks/prometheus_high_card/go.mod index a6d2b78e072..1e3709bbe66 100644 --- a/devenv/docker/blocks/prometheus_high_card/go.mod +++ b/devenv/docker/blocks/prometheus_high_card/go.mod @@ -1,6 +1,6 @@ module high-card -go 1.24.6 +go 1.25.3 require github.com/prometheus/client_golang v1.22.0 @@ -13,6 +13,6 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.16.1 // indirect - golang.org/x/sys v0.33.0 // indirect + golang.org/x/sys v0.36.0 // indirect google.golang.org/protobuf v1.36.6 // indirect ) diff --git a/devenv/docker/blocks/prometheus_high_card/go.sum b/devenv/docker/blocks/prometheus_high_card/go.sum index ebbd18eaeef..d2e136dfd38 100644 --- a/devenv/docker/blocks/prometheus_high_card/go.sum +++ b/devenv/docker/blocks/prometheus_high_card/go.sum @@ -24,8 +24,8 @@ github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzM github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/devenv/docker/blocks/prometheus_utf8/go.mod b/devenv/docker/blocks/prometheus_utf8/go.mod index 7f7bb1f3b44..2ecc8267f24 100644 --- a/devenv/docker/blocks/prometheus_utf8/go.mod +++ b/devenv/docker/blocks/prometheus_utf8/go.mod @@ -1,6 +1,6 @@ module utf8-support -go 1.24.6 +go 1.25.3 require ( github.com/prometheus/client_golang v1.22.0 @@ -15,6 +15,6 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/procfs v0.16.1 // indirect - golang.org/x/sys v0.33.0 // indirect + golang.org/x/sys v0.36.0 // indirect google.golang.org/protobuf v1.36.6 // indirect ) diff --git a/devenv/docker/blocks/prometheus_utf8/go.sum b/devenv/docker/blocks/prometheus_utf8/go.sum index ebbd18eaeef..d2e136dfd38 100644 --- a/devenv/docker/blocks/prometheus_utf8/go.sum +++ b/devenv/docker/blocks/prometheus_utf8/go.sum @@ -24,8 +24,8 @@ github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzM github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/devenv/docker/blocks/stateful_webhook/Dockerfile b/devenv/docker/blocks/stateful_webhook/Dockerfile index 258eec3d891..f4eaaa14256 100644 --- a/devenv/docker/blocks/stateful_webhook/Dockerfile +++ b/devenv/docker/blocks/stateful_webhook/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.24.6 +FROM golang:1.25.3 ADD main.go /go/src/webhook/main.go diff --git a/docs/sources/administration/roles-and-permissions/_index.md b/docs/sources/administration/roles-and-permissions/_index.md index eb2850eab15..d6cca1b7eb9 100644 --- a/docs/sources/administration/roles-and-permissions/_index.md +++ b/docs/sources/administration/roles-and-permissions/_index.md @@ -119,7 +119,7 @@ You can specify the following permissions to dashboards and folders. - **Edit**: Can create, edit, or delete a dashboard. Can edit or delete a folder, and create dashboards and subfolders in a folder. Editors _cannot_ change folder or dashboard permissions. - **View**: Can only view dashboards and folders. -> Important: When a user creates a dashboard or a folder they are automatically granted **Admin** permissions for it. +> Important: When a user creates a dashboard or folder at the top level, they are automatically granted Admin permissions for it. This does not apply to dashboards within a folder or to subfolders. For more information about assigning dashboard folder permissions, refer to [Grant dashboard folder permissions](../user-management/manage-dashboard-permissions/#grant-dashboard-folder-permissions). diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md index 1495886a032..43b6e9c74a1 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md @@ -181,3 +181,15 @@ The following table lists the default RBAC OnCall role assignments to the basic | Admin | `plugins:grafana-oncall-app:admin` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. | | Editor | `plugins:grafana-oncall-app:editor` | Default [Editor](ref:rbac-basic-roles) assignments. | | Viewer | `plugins:grafana-oncall-app:reader` | Default [Viewer](ref:rbac-basic-roles) assignments. | + +### Private data source connect roles + +The following table lists how private data source connect fixed roles are assigned to the basic roles: + +| Basic role | Associated fixed roles | Description | +| ------------- | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Grafana Admin | `plugins:grafana-pdc-app.private-networks:write`, `plugins:grafana-pdc-app.private-networks:read` | Default [Grafana server administrator](/docs/grafana//administration/roles-and-permissions/#grafana-server-administrators) assignments. | + +{{< admonition type="note" >}} +These private data source connect fixed roles must be granted alongside the `fixed:datasources:writer` role for the permissions to take effect. +{{< /admonition >}} diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md index 7ffb3c3d26a..de442b3d5d6 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-for-app-plugins/index.md @@ -5,6 +5,7 @@ description: Learn about how to configure access to app plugins using RBAC labels: products: - cloud + - enterprise menuTitle: RBAC for app plugins title: RBAC for app plugins weight: 90 @@ -39,7 +40,7 @@ refs: # RBAC for app plugins {{< admonition type="note" >}} -Available in [Grafana Cloud](/docs/grafana-cloud). +Available in [Grafana Enterprise](https://grafana.com/docs/grafana//introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud). {{< /admonition >}} RBAC can be used to manage access to [app plugins](https://grafana.com/docs/grafana/latest/administration/plugin-management/#app-plugins). diff --git a/docs/sources/administration/roles-and-permissions/access-control/rbac-terraform-provisioning/index.md b/docs/sources/administration/roles-and-permissions/access-control/rbac-terraform-provisioning/index.md index 8dde070b315..3f3f6da58b0 100644 --- a/docs/sources/administration/roles-and-permissions/access-control/rbac-terraform-provisioning/index.md +++ b/docs/sources/administration/roles-and-permissions/access-control/rbac-terraform-provisioning/index.md @@ -95,7 +95,7 @@ provider "grafana" { ## Provision basic roles -The following example shows how to assign basic roles to users, teams, and service accounts. Basic roles are predefined in Grafana and provide a set of permissions for common use cases. +The following example shows how to assign basic roles to users and service accounts. Basic roles are predefined in Grafana and provide a set of permissions for common use cases. | Basic role | UID | | --------------- | --------------------- | @@ -107,11 +107,11 @@ The following example shows how to assign basic roles to users, teams, and servi You can use any of the basic role UIDs from the table above in your role assignments. For example, to assign the "None" role, use `basic_none` as the `role_uid`. -```terraform -resource "grafana_team" "viewer_team" { - name = "terraform_viewer_team" -} +{{< admonition type="note" >}} +You can't assign basic roles to teams. To grant team permissions, assign a fixed or custom role to the team. +{{< /admonition >}} +```terraform resource "grafana_user" "editor_user" { email = "terraform_editor@example.com" login = "terraform_editor_user" @@ -122,12 +122,6 @@ resource "grafana_service_account" "admin_sa" { name = "terraform_admin_sa" } -# Assign Viewer role to a team -resource "grafana_role_assignment" "viewer_role_assignment" { - role_uid = "basic_viewer" - teams = [grafana_team.viewer_team.id] -} - # Assign Editor role to a user resource "grafana_role_assignment" "editor_role_assignment" { role_uid = "basic_editor" @@ -141,6 +135,22 @@ resource "grafana_role_assignment" "admin_role_assignment" { } ``` +### Assign a fixed or custom role to a team + +Use fixed or custom roles to grant permissions to teams: + +```terraform +resource "grafana_team" "writers_team" { + name = "terraform_writers_team" +} + +# Assign a fixed role to a team +resource "grafana_role_assignment" "writers_team_fixed_role" { + role_uid = "fixed:dashboards:writer" + teams = [grafana_team.writers_team.id] +} +``` + ### Provision basic role to multiple users ```terraform diff --git a/docs/sources/alerting/alerting-rules/templates/_index.md b/docs/sources/alerting/alerting-rules/templates/_index.md index f66781f01e5..a2afde85de7 100644 --- a/docs/sources/alerting/alerting-rules/templates/_index.md +++ b/docs/sources/alerting/alerting-rules/templates/_index.md @@ -220,24 +220,6 @@ To preview label values, select `Use notification policy`, and then click on `Pr {{< figure src="/media/docs/alerting/alert-instance-routing-preview.png" max-width="1200px" alt="Routing preview displays label values" >}} -## Grafana Cloud AI-generated templates - -Grafana Cloud users can use built-in AI tool to generate templates in the appropriate [alerting template language](/docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/language/) for you. - -To use AI to create your template, follow these steps: - -1. Go to **Alerting -> Contact points**. - -1. Click the Notification Templates tab then, click the **+ Add notification template group** button. - -1. Name your template. - -1. In the Template group section, click the **Generate with AI** button. - -1. Supply the AI tool with a prompt or select from one of the example prompts and edit that if necessary. - -1. Click **Save**. - ## More information For further details on how to template alert rules, refer to: diff --git a/docs/sources/alerting/configure-notifications/template-notifications/_index.md b/docs/sources/alerting/configure-notifications/template-notifications/_index.md index 0eb2ecb210d..75b110a9764 100644 --- a/docs/sources/alerting/configure-notifications/template-notifications/_index.md +++ b/docs/sources/alerting/configure-notifications/template-notifications/_index.md @@ -101,6 +101,24 @@ The notification template is assigned to the contact point to determine the noti By default, Grafana provides default templates, such as `{{define "default.title"}}` and `{{define "default.message"}}`, to format notification messages. +## Grafana Cloud AI-generated templates + +Grafana Cloud users can use built-in AI tool to generate templates in the appropriate [alerting template language](/docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/template-notifications/language/) for you. + +To use AI to create your template, follow these steps: + +1. Go to **Alerting -> Contact points**. + +1. Click the Notification Templates tab then, click the **+ Add notification template group** button. + +1. Name your template. + +1. In the Template group section, click the **Generate with AI** button. + +1. Supply the AI tool with a prompt or select from one of the example prompts and edit that if necessary. + +1. Click **Save**. + ## More information For further details on how to write notification templates, refer to: diff --git a/docs/sources/datasources/elasticsearch/_index.md b/docs/sources/datasources/elasticsearch/_index.md index 371d7282388..656182b251a 100644 --- a/docs/sources/datasources/elasticsearch/_index.md +++ b/docs/sources/datasources/elasticsearch/_index.md @@ -55,8 +55,9 @@ The following will help you get started working with Elasticsearch and Grafana: This data source supports these versions of Elasticsearch: -- v7.17+ +- ≥ v7.17 - v8.x +- v9.x Our maintenance policy for Elasticsearch data source is aligned with the [Elastic Product End of Life Dates](https://www.elastic.co/support/eol) and we ensure proper functionality for supported versions. If you are using an Elasticsearch with version that is past its end-of-life (EOL), you can still execute queries, but you will receive a notification in the query builder indicating that the version of Elasticsearch you are using is no longer supported. It's important to note that in such cases, we do not guarantee the correctness of the functionality, and we will not be addressing any related issues. diff --git a/docs/sources/fundamentals/getting-started/_index.md b/docs/sources/fundamentals/getting-started/_index.md index c453b3d9570..bc66d9796dc 100644 --- a/docs/sources/fundamentals/getting-started/_index.md +++ b/docs/sources/fundamentals/getting-started/_index.md @@ -2,7 +2,7 @@ aliases: - ../guides/what-is-grafana/ # /docs/grafana/latest/guides/what-is-grafana/ - ../getting-started/ # /docs/grafana/latest/getting-started/ -description: Learn how build your first dashboard after you have installed Grafana. +description: Learn how to build your first dashboard after you have installed Grafana. labels: products: - enterprise diff --git a/docs/sources/introduction/grafana-enterprise.md b/docs/sources/introduction/grafana-enterprise.md index ef8884f3c55..023aa5d6583 100644 --- a/docs/sources/introduction/grafana-enterprise.md +++ b/docs/sources/introduction/grafana-enterprise.md @@ -107,6 +107,7 @@ With a Grafana Enterprise license, you also get access to premium data sources, - [SAP HANA®](/grafana/plugins/grafana-saphana-datasource) - [ServiceNow](/grafana/plugins/grafana-servicenow-datasource) - [Snowflake](/grafana/plugins/grafana-snowflake-datasource) +- [SolarWinds](/grafana/plugins/grafana-solarwinds-datasource) - [Splunk](/grafana/plugins/grafana-splunk-datasource) - [Splunk Infrastructure monitoring (SignalFx)](/grafana/plugins/grafana-splunk-monitoring-datasource) - [Sqlyze Datasource](/grafana/plugins/grafana-odbc-datasource) diff --git a/docs/sources/setup-grafana/configure-grafana/_index.md b/docs/sources/setup-grafana/configure-grafana/_index.md index 9702851cd19..ff77ac47fc8 100644 --- a/docs/sources/setup-grafana/configure-grafana/_index.md +++ b/docs/sources/setup-grafana/configure-grafana/_index.md @@ -1641,11 +1641,35 @@ Syslog tag. By default, the process's `argv[0]` is used. #### `enabled` -Faro JavaScript agent is initialized. Default is `false`. +Grafana Faro instrumentation is initialized. Default is `false`. Enables the default set of instrumentations from `getWebInstrumentations`. See the options below to selectively disable some of these. #### `custom_endpoint` -Custom HTTP endpoint to send events captured by the Faro agent to. Default, `/log-grafana-javascript-agent`, logs the events to standard output. +Custom HTTP endpoint to send events captured by the Grafana Faro agent to. Default, `/log-grafana-javascript-agent`, logs the events to standard output. + +#### `api_key` + +If `custom_endpoint` required authentication, you can set the API key here. Only relevant for Grafana JavaScript Agent provider. + +#### `instrumentations_console_enabled` + +Enables the [Console instrumentation](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/console-instrumentation/) for Grafana Faro, defaults to `true`. + +#### `instrumentations_performance_enabled` + +Enables the [Performance instrumentation](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/performance-instrumentation/) for Grafana Faro, defaults to `true`. + +#### `instrumentations_csp_enabled` + +Enables the [Content Security Policy Violations instrumentation](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/csp-violation-tracking/) for Grafana Faro, defaults to `true`. + +#### `instrumentations_tracing_enabled` + +Enables the [Tracing instrumentation](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/tracing-instrumentation/) for Grafana Faro, defaults to `true`. + +#### `web_vitals_attribution_enabled` + +Enables sending [attribution data for web vitals](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/web-vitals/#web-vitals-attribution-data) with the Performance instrumentation, defaults to `true`. #### `log_endpoint_requests_per_second_limit` @@ -1655,30 +1679,6 @@ Requests per second limit enforced per an extended period, for Grafana backend l Maximum requests accepted per short interval of time for Grafana backend log ingestion endpoint, `/log-grafana-javascript-agent`. Default is `15`. -#### `instrumentations_all_enabled` - -Enables all Faro default instrumentation by using `getWebInstrumentations`. Overrides other instrumentation flags. - -#### `instrumentations_errors_enabled` - -Turn on error instrumentation. Only affects Grafana JavaScript Agent. - -#### `instrumentations_console_enabled` - -Turn on console instrumentation. Only affects Grafana JavaScript Agent - -#### `instrumentations_webvitals_enabled` - -Turn on web vitals instrumentation. Only affects Grafana JavaScript Agent - -#### `instrumentations_tracing_enabled` - -Turns on tracing instrumentation. Only affects Grafana JavaScript Agent. - -#### `api_key` - -If `custom_endpoint` required authentication, you can set the API key here. Only relevant for Grafana JavaScript Agent provider. -
### `[quota]` diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index e8b12113400..f4826193d63 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -109,6 +109,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `azureMonitorLogsBuilderEditor` | Enables the logs builder mode for the Azure Monitor data source | | `localeFormatPreference` | Specifies the locale so the correct format for numbers and dates can be shown | | `logsPanelControls` | Enables a control component for the logs panel in Explore | +| `grafanaPathfinder` | Enables Pathfinder app | | `azureResourcePickerUpdates` | Enables the updated Azure Monitor resource picker | ## Development feature toggles diff --git a/docs/sources/whatsnew/whats-new-in-v12-2.md b/docs/sources/whatsnew/whats-new-in-v12-2.md index bc8314d56be..64951665275 100644 --- a/docs/sources/whatsnew/whats-new-in-v12-2.md +++ b/docs/sources/whatsnew/whats-new-in-v12-2.md @@ -51,6 +51,9 @@ posts: - title: Authentication and authorization items: - whats-new/2025-09-10-scim-configuration-ui.md + - title: Auditing + items: + - whats-new/2025-10-09-auditing-options-for-recording-data-sources-queries.md whats_new_grafana_version: 12.2 weight: -51 --- 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 new file mode 100644 index 00000000000..ce5465c1e33 --- /dev/null +++ b/e2e-playwright/dashboard-new-layouts/dashboards-edit-custom-variables.spec.ts @@ -0,0 +1,212 @@ +import { Locator } from '@playwright/test'; + +import { test, expect, DashboardPage, E2ESelectorGroups } from '@grafana/plugin-e2e'; + +import { flows } from './utils'; + +test.use({ + featureToggles: { + kubernetesDashboards: true, + dashboardNewLayouts: true, + dashboardUndoRedo: true, + groupByVariable: true, + }, +}); + +test.use({ + viewport: { width: 1920, height: 1080 }, +}); + +const PAGE_UNDER_TEST = 'kVi2Gex7z/test-variable-output'; +const DASHBOARD_NAME = 'Test variable output'; + +test.describe( + 'Dashboard edit - Custom variable', + { + tag: ['@dashboards'], + }, + () => { + let addButton: Locator | undefined; + let rows: Locator | undefined; + let valueInputs: Locator | undefined; + let labelInputs: Locator | undefined; + let deleteButtons: Locator | undefined; + + const getAddButton = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups) => { + addButton = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.StaticOptionsEditor.addButton + ); + await expect(addButton).toBeVisible(); + }; + + const refetchItems = (dashboardPage: DashboardPage, selectors: E2ESelectorGroups) => { + rows = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.StaticOptionsEditor.row + ); + + valueInputs = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.StaticOptionsEditor.valueInput + ); + + labelInputs = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.StaticOptionsEditor.labelInput + ); + + deleteButtons = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.StaticOptionsEditor.deleteButton + ); + }; + + const checkRows = async (length: number) => { + expect(await rows!.all()).toHaveLength(length); + }; + + const fillValue = async (text: string, index: number) => { + await valueInputs!.nth(index).fill(text); + }; + + const fillLabel = async (text: string, index: number) => { + await labelInputs!.nth(index).fill(text); + }; + + const fillLabelValue = async (value: string, label: string, index: number) => { + await fillValue(value, index); + await fillLabel(label, index); + }; + + const openModal = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups) => { + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.optionsOpenButton) + .click(); + + await getAddButton(dashboardPage, selectors); + + refetchItems(dashboardPage, selectors); + }; + + const closeModal = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups) => { + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.closeButton) + .click(); + }; + + const checkItems = async (items: Array<[string, string?]>) => { + for (let i = 0; i < items.length; i++) { + const [value, label] = items[i]; + await expect(valueInputs!.nth(i)).toHaveValue(value); + await expect(labelInputs!.nth(i)).toHaveValue(label ?? ''); + } + }; + + const checkPreview = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups, labels: string[]) => { + const previewOptions = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.Settings.Variables.Edit.General.previewOfValuesOption + ); + + for (let i = 0; i < labels.length; i++) { + expect(await previewOptions.nth(i).textContent()).toBe(labels[i]); + } + }; + + const addItem = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups, value = '', label = '') => { + await addButton!.click(); + refetchItems(dashboardPage, selectors); + await fillLabelValue(value ?? '', label ?? '', (await rows!.all()).length - 1); + }; + + const removeItem = async (dashboardPage: DashboardPage, selectors: E2ESelectorGroups, index: number) => { + await deleteButtons!.nth(index).click(); + refetchItems(dashboardPage, selectors); + }; + + test.beforeEach(() => { + valueInputs = undefined; + labelInputs = undefined; + deleteButtons = undefined; + }); + + test('can add a new custom variable', async ({ gotoDashboardPage, selectors, page }) => { + const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST }); + await expect(page.getByText(DASHBOARD_NAME)).toBeVisible(); + + // common steps to add a new variable + await flows.newEditPaneVariableClick(dashboardPage, selectors); + await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, { + type: 'custom', + name: 'foo', + label: 'Foo', + value: '', + }); + + await openModal(dashboardPage, selectors); + await checkRows(1); + await addItem(dashboardPage, selectors); + await checkRows(2); + await fillValue('first value', 0); + await fillLabelValue('second value', 'second label', 1); + await addItem(dashboardPage, selectors, 'third value', 'third label'); + await addItem(dashboardPage, selectors, 'fourth value', 'fourth value'); + await removeItem(dashboardPage, selectors, 2); + await checkRows(3); + await checkPreview(dashboardPage, selectors, ['first value', 'second label', 'fourth value']); + await closeModal(dashboardPage, selectors); + + // assert variable is visible and has the correct values + const variableLabel = dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.SubMenu.submenuItemLabels('Foo') + ); + await expect(variableLabel).toBeVisible(); + await expect(variableLabel).toContainText('Foo'); + await expect( + dashboardPage.getByGrafanaSelector( + selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('first value') + ) + ).toBeVisible(); + + // check that variable deletion works + await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.deleteButton).click(); + await expect(variableLabel).toBeHidden(); + }); + + test('can edit a custom variable', async ({ gotoDashboardPage, selectors, page }) => { + const dashboardPage = await gotoDashboardPage({ + uid: PAGE_UNDER_TEST, + queryParams: new URLSearchParams({ orgId: '1', editview: 'variables' }), + }); + await expect(page.getByText(DASHBOARD_NAME)).toBeVisible(); + + // Create a custom variable in the dashboard settings page + await dashboardPage.getByGrafanaSelector(selectors.components.CallToActionCard.buttonV2('Add variable')).click(); + const typeSelect = dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.generalTypeSelectV2) + .locator('input'); + await typeSelect.fill('Custom'); + await typeSelect.press('Enter'); + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.generalNameInputV2) + .fill('foo'); + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.generalLabelInputV2) + .fill('Foo'); + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput) + .fill('first value, second label : second value, fourth value : fourth value'); + await dashboardPage + .getByGrafanaSelector(selectors.pages.Dashboard.Settings.Variables.Edit.General.applyButton) + .click(); + await dashboardPage + .getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton) + .click(); + + // Open the modal editor in the side pane + await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.Outline.section).click(); + await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.Outline.node('Variables')).click(); + await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.Outline.item('foo')).click(); + await openModal(dashboardPage, selectors); + + // Check the items + await checkItems([['first value'], ['second value', 'second label'], ['fourth value']]); + await checkPreview(dashboardPage, selectors, ['first value', 'second label', 'fourth value']); + }); + } +); diff --git a/e2e-playwright/dashboard-new-layouts/dashboards-edit-variables.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboards-edit-variables.spec.ts index 54991dfd623..4b5ea9574f8 100644 --- a/e2e-playwright/dashboard-new-layouts/dashboards-edit-variables.spec.ts +++ b/e2e-playwright/dashboard-new-layouts/dashboards-edit-variables.spec.ts @@ -20,46 +20,6 @@ test.describe( tag: ['@dashboards'], }, () => { - test('can add a new custom variable', async ({ gotoDashboardPage, selectors, page }) => { - const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST }); - await expect(page.getByText(DASHBOARD_NAME)).toBeVisible(); - - const variable: Variable = { - type: 'custom', - name: 'foo', - label: 'Foo', - value: 'one,two,three', - }; - - // common steps to add a new variable - await flows.newEditPaneVariableClick(dashboardPage, selectors); - await flows.newEditPanelCommonVariableInputs(dashboardPage, selectors, variable); - - // set the custom variable value - const customValueInput = dashboardPage.getByGrafanaSelector( - selectors.pages.Dashboard.Settings.Variables.Edit.CustomVariable.customValueInput - ); - await customValueInput.fill(variable.value); - await customValueInput.blur(); - - // assert the dropdown for the variable is visible and has the correct values - const variableLabel = dashboardPage.getByGrafanaSelector( - selectors.pages.Dashboard.SubMenu.submenuItemLabels(variable.label!) - ); - await expect(variableLabel).toBeVisible(); - await expect(variableLabel).toContainText(variable.label!); - - const values = variable.value.split(','); - const firstValueLink = dashboardPage.getByGrafanaSelector( - selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts(values[0]) - ); - await expect(firstValueLink).toBeVisible(); - - // check that variable deletion works - await dashboardPage.getByGrafanaSelector(selectors.components.EditPaneHeader.deleteButton).click(); - await expect(variableLabel).toBeHidden(); - }); - test('can add a new constant variable', async ({ gotoDashboardPage, selectors, page }) => { const dashboardPage = await gotoDashboardPage({ uid: PAGE_UNDER_TEST }); await expect(page.getByText(DASHBOARD_NAME)).toBeVisible(); diff --git a/e2e-playwright/dashboard-new-layouts/dashboards-repeats-custom-grid.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboards-repeats-custom-grid.spec.ts index 0a349df5728..2d235571f97 100644 --- a/e2e-playwright/dashboard-new-layouts/dashboards-repeats-custom-grid.spec.ts +++ b/e2e-playwright/dashboard-new-layouts/dashboards-repeats-custom-grid.spec.ts @@ -1,6 +1,5 @@ import { test, expect } from '@grafana/plugin-e2e'; -import testV2Dashboard from '../dashboards/TestV2Dashboard.json'; import testV2DashWithRepeats from '../dashboards/V2DashWithRepeats.json'; import { diff --git a/e2e-playwright/dashboard-new-layouts/dashboards-repeats-tabs-layout.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboards-repeats-tabs-layout.spec.ts new file mode 100644 index 00000000000..100ec864b0d --- /dev/null +++ b/e2e-playwright/dashboard-new-layouts/dashboards-repeats-tabs-layout.spec.ts @@ -0,0 +1,487 @@ +import { test, expect } from '@grafana/plugin-e2e'; + +import V2DashWithTabRepeats from '../dashboards/V2DashWithTabRepeats.json'; + +import { + verifyChanges, + saveDashboard, + importTestDashboard, + goToEmbeddedPanel, + checkRepeatedTabTitles, + groupIntoTab, + moveTab, + getTabPosition, +} from './utils'; + +const repeatTitleBase = 'Tab - '; +const newTitleBase = 'edited tab rep - '; +const repeatOptions = [1, 2, 3, 4]; + +test.use({ + featureToggles: { + kubernetesDashboards: true, + dashboardNewLayouts: true, + groupByVariable: true, + }, +}); + +test.use({ + viewport: { width: 1920, height: 1080 }, +}); + +test.describe( + 'Repeats - Dashboard tabs layout', + { + tag: ['@dashboards'], + }, + () => { + test('can enable tab repeats', async ({ dashboardPage, selectors, page }) => { + await importTestDashboard(page, selectors, 'Tabs layout repeats - add repeats'); + + await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click(); + + await groupIntoTab(page, dashboardPage, selectors); + + await dashboardPage + .getByGrafanaSelector(selectors.components.PanelEditor.ElementEditPane.TabsLayout.titleInput) + .fill(`${repeatTitleBase}$c1`); + + await expect( + dashboardPage.getByGrafanaSelector( + selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.join(' + ')}`) + ) + ).toBeVisible(); + + const repeatOptionsGroup = dashboardPage.getByGrafanaSelector( + selectors.components.OptionsGroup.group('repeat-options') + ); + // expand repeat options dropdown + await repeatOptionsGroup.getByRole('button').first().click(); + // find repeat variable dropdown + await repeatOptionsGroup.getByRole('combobox').click(); + await page.getByRole('option', { name: 'c1' }).click(); + + await checkRepeatedTabTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions); + + await saveDashboard(dashboardPage, page, selectors); + await page.reload(); + + await checkRepeatedTabTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions); + }); + + test('can update tab repeats with variable change', async ({ dashboardPage, selectors, page }) => { + await importTestDashboard( + page, + selectors, + 'Tabs layout repeats - update on variable change', + JSON.stringify(V2DashWithTabRepeats) + ); + + const c1Var = dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.SubMenu.submenuItemLabels('c1')); + await c1Var + .locator('..') + .getByTestId(selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts(repeatOptions.join(','))) + .click(); + // deselect last variable option + await dashboardPage + .getByGrafanaSelector( + selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts(`${repeatOptions.at(-1)}`) + ) + .click(); + await page.locator('body').click({ position: { x: 0, y: 0 } }); // blur select + + // verify that repeats are present for first 3 values + await checkRepeatedTabTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions.slice(0, -1)); + // verify there is no repeat with last value + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(-1)}`)) + ).toBeHidden(); + }); + test('can update repeats in edit pane', async ({ dashboardPage, selectors, page }) => { + await importTestDashboard( + page, + selectors, + 'Tabs layout repeats - update through edit pane', + JSON.stringify(V2DashWithTabRepeats) + ); + + await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click(); + // select first/original repeat tab to activate edit pane + await dashboardPage + .getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(0)}`)) + .click(); + + const titleInput = dashboardPage.getByGrafanaSelector( + selectors.components.PanelEditor.ElementEditPane.TabsLayout.titleInput + ); + await titleInput.fill(`${newTitleBase}$c1`); + await titleInput.blur(); + + await checkRepeatedTabTitles(dashboardPage, selectors, newTitleBase, repeatOptions); + + await saveDashboard(dashboardPage, page, selectors); + await page.reload(); + + await checkRepeatedTabTitles(dashboardPage, selectors, newTitleBase, repeatOptions); + }); + + test('can update repeats after panel change', async ({ dashboardPage, selectors, page }) => { + await importTestDashboard( + page, + selectors, + 'Tabs layout repeats - update repeats after panel change', + JSON.stringify(V2DashWithTabRepeats) + ); + + await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click(); + + await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first().click(); + + const panelTitleInput = dashboardPage.getByGrafanaSelector( + selectors.components.PanelEditor.OptionsPane.fieldInput('Title') + ); + await panelTitleInput.fill('New edited panel'); + await panelTitleInput.blur(); + + await dashboardPage + .getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(1)}`)) + .click(); + + // intermediate step to verify tab switch happened + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 2 - Row 1 - Panel repeat 1')) + ).toBeVisible(); + + // verify edited panel title updated in repeated tab + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New edited panel')) + ).toBeVisible(); + + await saveDashboard(dashboardPage, page, selectors); + await page.reload(); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New edited panel')) + ).toBeVisible(); + }); + + test('can update repeats after panel change in editor', async ({ dashboardPage, selectors, page }) => { + await importTestDashboard( + page, + selectors, + 'Tabs layout repeats - update repeats after panel change in editor', + JSON.stringify(V2DashWithTabRepeats) + ); + + const panel = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first(); + await panel.hover(); + await page.keyboard.press('e'); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.DashboardEditPaneSplitter.primaryBody) + ).toBeHidden(); // verifying that panel editor loaded + + const panelTitleInput = dashboardPage.getByGrafanaSelector( + selectors.components.PanelEditor.OptionsPane.fieldInput('Title') + ); + await panelTitleInput.fill('New edited panel'); + await panelTitleInput.blur(); + + // playwright too fast, verifying JSON diff that changes landed + await verifyChanges(dashboardPage, page, selectors, 'New edited panel'); + + // verify panel title change in panel editor UI + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(`New edited panel`)) + ).toBeVisible(); + + await dashboardPage + .getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton) + .click(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.DashboardEditPaneSplitter.primaryBody) + ).toBeVisible(); // verifying that dashboard loaded + + await dashboardPage + .getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(1)}`)) + .click(); + + // intermediate step to verify tab switch happened + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 2 - Row 1 - Panel repeat 1')) + ).toBeVisible(); + + // verify edited panel title updated in repeated tab + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New edited panel')) + ).toBeVisible(); + + await saveDashboard(dashboardPage, page, selectors); + await page.reload(); + + // verify edited panel title updated in repeated tab + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New edited panel')) + ).toBeVisible(); + }); + + test('can hide canvas grid add row action in repeats', async ({ dashboardPage, selectors, page }) => { + await importTestDashboard( + page, + selectors, + 'Tabs layout repeats - hide canvas add action in repeats', + JSON.stringify(V2DashWithTabRepeats) + ); + + await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click(); + + await expect(dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow)).toBeVisible(); + + await dashboardPage + .getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(1)}`)) + .click(); + + await expect(dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.addRow)).toBeHidden(); + }); + + test('can move repeated tabs', async ({ dashboardPage, selectors, page }) => { + await importTestDashboard( + page, + selectors, + 'Tabs layout repeats - move repeated tabs', + JSON.stringify(V2DashWithTabRepeats) + ); + await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click(); + await moveTab(dashboardPage, page, selectors, `${repeatTitleBase}${repeatOptions.at(0)}`, 'New tab'); + + // playwright too fast - adding intermediate step so that UI has time to update + await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab')).click(); + + // verify move by tab position + const repeatedTab = await getTabPosition(dashboardPage, selectors, `${repeatTitleBase}${repeatOptions.at(0)}`); + const normalTab = await getTabPosition(dashboardPage, selectors, 'New tab'); + expect(normalTab?.x).toBeLessThan(repeatedTab?.x || 0); + await saveDashboard(dashboardPage, page, selectors); + await page.reload(); + + const repeatedTab2 = await getTabPosition(dashboardPage, selectors, `${repeatTitleBase}${repeatOptions.at(0)}`); + const normalTab2 = await getTabPosition(dashboardPage, selectors, 'New tab'); + expect(normalTab2?.x).toBeLessThan(repeatedTab2?.x || 0); + }); + + test('can load into repeated tab', async ({ dashboardPage, selectors, page }) => { + await importTestDashboard( + page, + selectors, + 'Tabs layout repeats - can load into repeated tab', + JSON.stringify(V2DashWithTabRepeats) + ); + + await dashboardPage + .getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`)) + .click(); + + await page.reload(); + + await expect(page.locator('[data-testid="uplot-main-div"]').first()).toBeVisible(); + + expect( + await dashboardPage + .getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`)) + .getAttribute('aria-selected') + ).toBe('true'); + }); + + test('can view panels in repeated tab', async ({ dashboardPage, selectors, page }) => { + await importTestDashboard( + page, + selectors, + 'Tabs layout repeats - view panels in repeated tabs', + JSON.stringify(V2DashWithTabRepeats) + ); + + // non repeated panel in repeated tab + await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first().hover(); + await page.keyboard.press('v'); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 1 - Panel repeat 1')) + ).toBeHidden(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) + ).toBeVisible(); + + await page.reload(); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) + ).toBeVisible(); + + await dashboardPage + .getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton) + .click(); + + // repeated panel in original tab repeat + await dashboardPage + .getByGrafanaSelector(selectors.components.DashboardRow.title('Row 2')) + .scrollIntoViewIfNeeded(); + await dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 2 - Panel repeat 2')) + .hover(); + await page.keyboard.press('v'); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 1 - Panel repeat 1')) + ).toBeHidden(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 2 - Panel repeat 2')) + ).toBeVisible(); + + await page.reload(); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 2 - Panel repeat 2')) + ).toBeVisible(); + + await dashboardPage + .getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.backToDashboardButton) + .click(); + + // repeated panel in repeated tab + await dashboardPage + .getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`)) + .click(); + await dashboardPage + .getByGrafanaSelector(selectors.components.DashboardRow.title('Row 2')) + .scrollIntoViewIfNeeded(); + await dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 2 - Panel repeat 2')) + .hover(); + await page.keyboard.press('v'); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 1 - Panel repeat 1')) + ).toBeHidden(); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 2 - Panel repeat 2')) + ).toBeVisible(); + + await page.reload(); + + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 2 - Panel repeat 2')) + ).toBeVisible(); + }); + + test('can view embedded panels in repeated tab', async ({ dashboardPage, selectors, page }) => { + await importTestDashboard( + page, + selectors, + 'Tabs layout repeats - view embedded panels in repeated tabs', + JSON.stringify(V2DashWithTabRepeats) + ); + + const dashUrl = page.url(); + + // non repeated panel in repeated tab + await dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')).first().hover(); + await page.keyboard.press('p+e'); + await goToEmbeddedPanel(page); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('New panel')) + ).toBeVisible(); + await page.goto(dashUrl); + + // repeated panel in original tab repeat + await dashboardPage + .getByGrafanaSelector(selectors.components.DashboardRow.title('Row 2')) + .scrollIntoViewIfNeeded(); + await dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 2 - Panel repeat 2')) + .hover(); + await page.keyboard.press('p+e'); + await goToEmbeddedPanel(page); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 1 - Row 2 - Panel repeat 2')) + ).toBeVisible(); + await page.goto(dashUrl); + + // repeated panel in repeated tab + await dashboardPage + .getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`)) + .click(); + await dashboardPage + .getByGrafanaSelector(selectors.components.DashboardRow.title('Row 2')) + .scrollIntoViewIfNeeded(); + await dashboardPage + .getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 2 - Panel repeat 2')) + .hover(); + await page.keyboard.press('p+e'); + await goToEmbeddedPanel(page); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Tab 3 - Row 2 - Panel repeat 2')) + ).toBeVisible(); + }); + + test('can remove repeats', async ({ dashboardPage, selectors, page }) => { + await importTestDashboard( + page, + selectors, + 'Tabs layout repeats - remove repeats', + JSON.stringify(V2DashWithTabRepeats) + ); + + // verify 5 tabs are present (4 repeats and 1 normal) + await checkRepeatedTabTitles(dashboardPage, selectors, repeatTitleBase, repeatOptions); + await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible(); + + await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.editDashboard.editButton).click(); + + await dashboardPage + .getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(0)}`)) + .click(); + + const repeatOptionsGroup = dashboardPage.getByGrafanaSelector( + selectors.components.OptionsGroup.group('repeat-options') + ); + // expand repeat options dropdown + await repeatOptionsGroup.getByRole('button').first().click(); + // find repeat variable dropdown + await repeatOptionsGroup.getByRole('combobox').click(); + await page.getByRole('option', { name: 'Disable repeating' }).click(); + + await expect( + dashboardPage.getByGrafanaSelector( + selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.join(' + ')}`) + ) + ).toBeVisible(); + await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(1)}`)) + ).toBeHidden(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`)) + ).toBeHidden(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(3)}`)) + ).toBeHidden(); + + await saveDashboard(dashboardPage, page, selectors); + await page.reload(); + + await expect( + dashboardPage.getByGrafanaSelector( + selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.join(' + ')}`) + ) + ).toBeVisible(); + await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('New tab'))).toBeVisible(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(1)}`)) + ).toBeHidden(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(2)}`)) + ).toBeHidden(); + await expect( + dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${repeatTitleBase}${repeatOptions.at(3)}`)) + ).toBeHidden(); + }); + } +); diff --git a/e2e-playwright/dashboard-new-layouts/utils.ts b/e2e-playwright/dashboard-new-layouts/utils.ts index 2d279b0545a..70f5aa41bf0 100644 --- a/e2e-playwright/dashboard-new-layouts/utils.ts +++ b/e2e-playwright/dashboard-new-layouts/utils.ts @@ -187,3 +187,48 @@ export async function goToEmbeddedPanel(page: Page) { page.goto(soloPanelUrl!); } + +export async function moveTab( + dashboardPage: DashboardPage, + page: Page, + selectors: E2ESelectorGroups, + sourceTab: string, + targetTab: string +) { + // Get target panel position + const targetTabElement = dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(targetTab)).first(); + + // Get source panel element + const sourceTabElement = dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(sourceTab)).first(); + + const targetBox = await targetTabElement.boundingBox(); + + // Perform drag and drop (dragTo() did not work in this case) + await sourceTabElement.hover(); + await page.mouse.down(); + // move to adjusted target position (relative to top left) + await page.mouse.move((targetBox?.x || 0) + (targetBox?.width || 0), targetBox?.y || 0, { steps: 5 }); + await page.mouse.up(); +} + +export async function groupIntoTab(page: Page, dashboardPage: DashboardPage, selectors: E2ESelectorGroups) { + await dashboardPage.getByGrafanaSelector(selectors.components.CanvasGridAddActions.groupPanels).click(); + await page.getByText('Group into tab').click(); +} + +export async function checkRepeatedTabTitles( + dashboardPage: DashboardPage, + selectors: E2ESelectorGroups, + title: string, + options: Array +) { + for (const option of options) { + await expect(dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(`${title}${option}`))).toBeVisible(); + } +} + +export async function getTabPosition(dashboardPage: DashboardPage, selectors: E2ESelectorGroups, tabTitle: string) { + const tab = dashboardPage.getByGrafanaSelector(selectors.components.Tab.title(tabTitle)).first(); + const boundingBox = await tab.boundingBox(); + return boundingBox; +} diff --git a/e2e-playwright/dashboards/V2DashWithTabRepeats.json b/e2e-playwright/dashboards/V2DashWithTabRepeats.json new file mode 100644 index 00000000000..99cabae8d94 --- /dev/null +++ b/e2e-playwright/dashboards/V2DashWithTabRepeats.json @@ -0,0 +1,837 @@ +{ + "apiVersion": "dashboard.grafana.app/v2beta1", + "kind": "Dashboard", + "metadata": { + "name": "addwm76", + "namespace": "default", + "uid": "TOro1wyxnlZJEWBZN3MF2MuXtdBb1GOnG6zynaCifi4X", + "resourceVersion": "3", + "generation": 4, + "creationTimestamp": "2025-09-18T08:03:45Z", + "labels": {}, + "annotations": {} + }, + "spec": { + "annotations": [], + "cursorSync": "Off", + "description": "", + "editable": true, + "elements": { + "panel-1": { + "kind": "Panel", + "spec": { + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "PD8C576611E62080A" + }, + "group": "grafana-testdata-datasource", + "kind": "DataQuery", + "spec": {}, + "version": "v0" + }, + "refId": "A" + } + } + ], + "queryOptions": {}, + "transformations": [] + } + }, + "description": "", + "id": 1, + "links": [], + "title": "Tab $c1 - Row $c2 - Panel repeat $c3", + "vizConfig": { + "group": "timeseries", + "kind": "VizConfig", + "spec": { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + } + }, + "version": "12.3.0-pre" + } + } + }, + "panel-2": { + "kind": "Panel", + "spec": { + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "PD8C576611E62080A" + }, + "group": "grafana-testdata-datasource", + "kind": "DataQuery", + "spec": { + "scenarioId": "random_walk", + "seriesCount": 1 + }, + "version": "v0" + }, + "refId": "A" + } + } + ], + "queryOptions": {}, + "transformations": [] + } + }, + "description": "", + "id": 2, + "links": [], + "title": "New panel", + "vizConfig": { + "group": "timeseries", + "kind": "VizConfig", + "spec": { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + } + }, + "version": "12.3.0-pre" + } + } + }, + "panel-3": { + "kind": "Panel", + "spec": { + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "PD8C576611E62080A" + }, + "group": "grafana-testdata-datasource", + "kind": "DataQuery", + "spec": {}, + "version": "v0" + }, + "refId": "A" + } + } + ], + "queryOptions": {}, + "transformations": [] + } + }, + "description": "", + "id": 3, + "links": [], + "title": "New panel", + "vizConfig": { + "group": "timeseries", + "kind": "VizConfig", + "spec": { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + } + }, + "version": "12.3.0-pre" + } + } + }, + "panel-4": { + "kind": "Panel", + "spec": { + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "PD8C576611E62080A" + }, + "group": "grafana-testdata-datasource", + "kind": "DataQuery", + "spec": {}, + "version": "v0" + }, + "refId": "A" + } + } + ], + "queryOptions": {}, + "transformations": [] + } + }, + "description": "", + "id": 4, + "links": [], + "title": "New panel", + "vizConfig": { + "group": "timeseries", + "kind": "VizConfig", + "spec": { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + } + }, + "version": "12.3.0-pre" + } + } + }, + "panel-5": { + "kind": "Panel", + "spec": { + "data": { + "kind": "QueryGroup", + "spec": { + "queries": [ + { + "kind": "PanelQuery", + "spec": { + "hidden": false, + "query": { + "datasource": { + "name": "PD8C576611E62080A" + }, + "group": "grafana-testdata-datasource", + "kind": "DataQuery", + "spec": {}, + "version": "v0" + }, + "refId": "A" + } + } + ], + "queryOptions": {}, + "transformations": [] + } + }, + "description": "", + "id": 5, + "links": [], + "title": "New panel", + "vizConfig": { + "group": "timeseries", + "kind": "VizConfig", + "spec": { + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + } + }, + "version": "12.3.0-pre" + } + } + } + }, + "layout": { + "kind": "TabsLayout", + "spec": { + "tabs": [ + { + "kind": "TabsLayoutTab", + "spec": { + "layout": { + "kind": "RowsLayout", + "spec": { + "rows": [ + { + "kind": "RowsLayoutRow", + "spec": { + "layout": { + "kind": "AutoGridLayout", + "spec": { + "columnWidthMode": "standard", + "items": [ + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-5" + } + } + } + ], + "maxColumnCount": 3, + "rowHeightMode": "standard" + } + }, + "title": "New row" + } + }, + { + "kind": "RowsLayoutRow", + "spec": { + "collapse": false, + "layout": { + "kind": "GridLayout", + "spec": { + "items": [ + { + "kind": "GridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-1" + }, + "height": 8, + "repeat": { + "direction": "h", + "mode": "variable", + "value": "c3" + }, + "width": 24, + "x": 0, + "y": 0 + } + }, + { + "kind": "GridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-2" + }, + "height": 8, + "width": 12, + "x": 0, + "y": 8 + } + }, + { + "kind": "GridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-3" + }, + "height": 8, + "width": 12, + "x": 12, + "y": 8 + } + } + ] + } + }, + "repeat": { + "mode": "variable", + "value": "c2" + }, + "title": "Row $c2" + } + } + ] + } + }, + "repeat": { + "mode": "variable", + "value": "c1" + }, + "title": "Tab - $c1" + } + }, + { + "kind": "TabsLayoutTab", + "spec": { + "layout": { + "kind": "AutoGridLayout", + "spec": { + "columnWidthMode": "standard", + "items": [ + { + "kind": "AutoGridLayoutItem", + "spec": { + "element": { + "kind": "ElementReference", + "name": "panel-4" + } + } + } + ], + "maxColumnCount": 3, + "rowHeightMode": "standard" + } + }, + "title": "New tab" + } + } + ] + } + }, + "links": [], + "liveNow": false, + "preload": false, + "tags": [], + "timeSettings": { + "autoRefresh": "", + "autoRefreshIntervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], + "fiscalYearStartMonth": 0, + "from": "now-6h", + "hideTimepicker": false, + "timezone": "browser", + "to": "now" + }, + "title": "tab repeats", + "variables": [ + { + "kind": "CustomVariable", + "spec": { + "allowCustomValue": true, + "current": { + "text": ["1", "2", "3", "4"], + "value": ["1", "2", "3", "4"] + }, + "hide": "dontHide", + "includeAll": true, + "multi": true, + "name": "c1", + "options": [ + { + "selected": true, + "text": "1", + "value": "1" + }, + { + "selected": true, + "text": "2", + "value": "2" + }, + { + "selected": true, + "text": "3", + "value": "3" + }, + { + "selected": true, + "text": "4", + "value": "4" + } + ], + "query": "1,2,3,4", + "skipUrlSync": false + } + }, + { + "kind": "CustomVariable", + "spec": { + "allowCustomValue": true, + "current": { + "text": ["1", "2", "3", "4"], + "value": ["1", "2", "3", "4"] + }, + "hide": "dontHide", + "includeAll": true, + "multi": true, + "name": "c2", + "options": [ + { + "selected": true, + "text": "1", + "value": "1" + }, + { + "selected": true, + "text": "2", + "value": "2" + }, + { + "selected": true, + "text": "3", + "value": "3" + }, + { + "selected": true, + "text": "4", + "value": "4" + } + ], + "query": "1,2,3,4", + "skipUrlSync": false + } + }, + { + "kind": "CustomVariable", + "spec": { + "allowCustomValue": true, + "current": { + "text": ["1", "2", "3", "4"], + "value": ["1", "2", "3", "4"] + }, + "hide": "dontHide", + "includeAll": false, + "multi": true, + "name": "c3", + "options": [ + { + "selected": true, + "text": "1", + "value": "1" + }, + { + "selected": true, + "text": "2", + "value": "2" + }, + { + "selected": true, + "text": "3", + "value": "3" + }, + { + "selected": true, + "text": "4", + "value": "4" + } + ], + "query": "1,2,3,4", + "skipUrlSync": false + } + } + ] + }, + "status": {} +} diff --git a/e2e/internal/cmd/cypress/cmd.go b/e2e/internal/cmd/cypress/cmd.go index ec4d836cd15..24b347c115c 100644 --- a/e2e/internal/cmd/cypress/cmd.go +++ b/e2e/internal/cmd/cypress/cmd.go @@ -109,10 +109,15 @@ func NewCmd() *cli.Command { }, &cli.BoolFlag{ Name: "image-renderer", - Usage: "Install the image renderer plugin (requires --start-grafana)", + Usage: "Start the image renderer Docker container (requires --start-grafana)", + Category: "Grafana Server", + }, + &cli.StringFlag{ + Name: "image-renderer-version", + Usage: "When enabling the image renderer, which version to use", + Value: "latest", Category: "Grafana Server", }, - &cli.StringFlag{ Name: "suite", Usage: "Path to the suite to run (e.g. './e2e/dashboards-suite')", @@ -194,7 +199,10 @@ func runAction(ctx context.Context, c *cli.Command) error { cmd.Env = os.Environ() cmd.Env = append(cmd.Env, fmt.Sprintf("TZ=%s", c.String("timezone"))) if c.Bool("image-renderer") { - cmd.Env = append(cmd.Env, "INSTALL_IMAGE_RENDERER=true") + cmd.Env = append(cmd.Env, "START_IMAGE_RENDERER=true") + } + if c.String("image-renderer-version") != "" { + cmd.Env = append(cmd.Env, fmt.Sprintf("IMAGE_RENDERER_VERSION=%s", c.String("image-renderer-version"))) } cmd.Stdout = prefixGrafana(os.Stdout) cmd.Stderr = prefixGrafana(os.Stderr) diff --git a/eslint-suppressions.json b/eslint-suppressions.json index c69363ff210..829f5944cff 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -2189,19 +2189,6 @@ "count": 1 } }, - "public/app/features/dashboard-scene/settings/variables/editors/QueryVariableEditor.tsx": { - "no-restricted-syntax": { - "count": 1 - } - }, - "public/app/features/dashboard-scene/settings/variables/utils.ts": { - "@typescript-eslint/consistent-type-assertions": { - "count": 1 - }, - "@typescript-eslint/no-explicit-any": { - "count": 1 - } - }, "public/app/features/dashboard-scene/sharing/ShareButton/share-externally/EmailShare/ConfigEmailSharing/ConfigEmailSharing.tsx": { "no-restricted-syntax": { "count": 1 @@ -2541,10 +2528,10 @@ }, "public/app/features/dashboard/state/DashboardMigrator.ts": { "@typescript-eslint/consistent-type-assertions": { - "count": 4 + "count": 2 }, "@typescript-eslint/no-explicit-any": { - "count": 24 + "count": 18 } }, "public/app/features/dashboard/state/DashboardModel.repeat.test.ts": { diff --git a/eslint.config.js b/eslint.config.js index a0245a55b48..cb8fba5222d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -100,6 +100,7 @@ module.exports = [ '**/build/', '**/compiled/', '**/dist/', + 'coverage/', 'data/', 'deployment_tools_config.json', 'devenv', diff --git a/go.mod b/go.mod index 5010002e3a8..8754c16fc81 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/grafana/grafana -go 1.24.6 +go 1.25.3 require ( buf.build/gen/go/parca-dev/parca/connectrpc/go v1.18.1-20250703125925-3f0fcf4bff96.1 // @grafana/observability-traces-and-profiling @@ -13,8 +13,8 @@ require ( filippo.io/age v1.2.1 // @grafana/identity-access-team github.com/1NCE-GmbH/grpc-go-pool v0.0.0-20231117122434-2a5bb974daa2 // @grafana/grafana-search-and-storage github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // @grafana/partner-datasources - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 // @grafana/identity-access-team - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 // @grafana/grafana-backend-group + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // @grafana/identity-access-team + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 // @grafana/grafana-backend-group github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 // @grafana/grafana-backend-group github.com/Azure/azure-storage-blob-go v0.15.0 // @grafana/grafana-backend-group github.com/Azure/go-autorest/autorest v0.11.29 // @grafana/grafana-backend-group @@ -67,7 +67,7 @@ require ( github.com/go-openapi/loads v0.22.0 // @grafana/alerting-backend github.com/go-openapi/runtime v0.28.0 // @grafana/alerting-backend github.com/go-openapi/strfmt v0.23.0 // @grafana/alerting-backend - github.com/go-redis/redis/v8 v8.11.5 // @grafana/grafana-backend-group + github.com/go-redis/redis/v8 v8.11.5 // indirect; @grafana/grafana-backend-group github.com/go-sourcemap/sourcemap v2.1.4+incompatible // @grafana/grafana-backend-group github.com/go-sql-driver/mysql v1.9.3 // @grafana/grafana-search-and-storage github.com/go-stack/stack v1.8.1 // @grafana/grafana-backend-group @@ -86,7 +86,7 @@ require ( github.com/googleapis/gax-go/v2 v2.14.2 // @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-20251007160934-e642236ea9eb // @grafana/alerting-backend + github.com/grafana/alerting v0.0.0-20251009192429-9427c24835ae // @grafana/alerting-backend github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f // @grafana/identity-access-team github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37 // @grafana/identity-access-team github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics @@ -99,7 +99,7 @@ require ( github.com/grafana/grafana-app-sdk v0.46.0 // @grafana/grafana-app-platform-squad github.com/grafana/grafana-app-sdk/logging v0.45.0 // @grafana/grafana-app-platform-squad github.com/grafana/grafana-aws-sdk v1.2.0 // @grafana/aws-datasources - github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 // @grafana/partner-datasources + github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1 // @grafana/partner-datasources github.com/grafana/grafana-cloud-migration-snapshot v1.9.0 // @grafana/grafana-operator-experience-squad github.com/grafana/grafana-google-sdk-go v0.4.2 // @grafana/partner-datasources github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 // @grafana/grafana-backend-group @@ -161,7 +161,7 @@ require ( github.com/prometheus/prometheus v0.303.1 // @grafana/alerting-backend github.com/prometheus/sigv4 v0.1.2 // @grafana/alerting-backend github.com/puzpuzpuz/xsync/v4 v4.2.0 // @grafana/grafana-backend-group - github.com/redis/go-redis/v9 v9.8.0 // @grafana/alerting-backend + github.com/redis/go-redis/v9 v9.14.0 // @grafana/alerting-backend github.com/robfig/cron/v3 v3.0.1 // @grafana/grafana-backend-group github.com/rs/cors v1.11.1 // @grafana/identity-access-team github.com/russellhaering/goxmldsig v1.4.0 // @grafana/grafana-backend-group @@ -202,7 +202,7 @@ require ( golang.org/x/crypto v0.42.0 // @grafana/grafana-backend-group golang.org/x/exp v0.0.0-20250911091902-df9299821621 // @grafana/alerting-backend golang.org/x/mod v0.28.0 // indirect; @grafana/grafana-backend-group - golang.org/x/net v0.44.0 // @grafana/oss-big-tent @grafana/partner-datasources + golang.org/x/net v0.45.0 // @grafana/oss-big-tent @grafana/partner-datasources golang.org/x/oauth2 v0.30.0 // @grafana/identity-access-team golang.org/x/sync v0.17.0 // @grafana/alerting-backend golang.org/x/text v0.29.0 // @grafana/grafana-backend-group @@ -296,7 +296,7 @@ require ( cloud.google.com/go/monitoring v1.24.2 // indirect cuelabs.dev/go/oci/ociregistry v0.0.0-20240906074133-82eb438dd565 // indirect github.com/Azure/azure-pipeline-go v0.2.3 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect @@ -306,7 +306,7 @@ require ( github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect github.com/FZambia/eagle v0.2.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect diff --git a/go.sum b/go.sum index a752393996b..acb8cdb6a37 100644 --- a/go.sum +++ b/go.sum @@ -648,14 +648,14 @@ github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1 h1:Wc1ml6QlJs2BHQ/9Bqu1jiyggbsSjramq2oUmp5WeIo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 h1:wL5IEG5zb7BVv1Kv0Xm92orq+5hB5Nipn3B5tn4Rqfk= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0 h1:m/sWOGCREuSBqg2htVQTBY8nOZpyajYztF0vUvSZTuM= github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.10.0/go.mod h1:Pu5Zksi2KrU7LPbZbNINx6fuVrUp/ffvpxdDj+i8LeE= github.com/Azure/azure-sdk-for-go/sdk/keyvault/internal v0.7.1 h1:FbH3BbSb4bvGluTesZZ+ttN/MDsnMmQP36OSnDuSXqw= @@ -703,8 +703,8 @@ github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= -github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 h1:XkkQbfMyuH2jTSjQjSoihryI8GINRcs4xp8lNawg0FI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWOFLaBCDVU5a9X/XikSRBosye++6gojw= github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -1585,8 +1585,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-20251007160934-e642236ea9eb h1:ejpL3pI9t1ebEFtxaP7TMDKZagCu1nSq1O8op+sO4DY= -github.com/grafana/alerting v0.0.0-20251007160934-e642236ea9eb/go.mod h1:VGjS5gDwWEADPP6pF/drqLxEImgeuHlEW5u8E5EfIrM= +github.com/grafana/alerting v0.0.0-20251009192429-9427c24835ae h1:NLPwY3tIP0lg0g9wTRiMcypm6VRXW6W+MOLBsq8JSVA= +github.com/grafana/alerting v0.0.0-20251009192429-9427c24835ae/go.mod h1:VGjS5gDwWEADPP6pF/drqLxEImgeuHlEW5u8E5EfIrM= 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-20250926065801-df98203cff37 h1:qEwZ+7MbPjzRvTi31iT9w7NBhKIpKwZrFbYmOZLqkwA= @@ -1611,8 +1611,8 @@ github.com/grafana/grafana-app-sdk/logging v0.45.0 h1:0SH6nYZpiLBZRwUq4J6+1vo8xu github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA= github.com/grafana/grafana-aws-sdk v1.2.0 h1:LLR4/g91WBuCRwm2cbWfCREq565+GxIFe08nqqIcIuw= github.com/grafana/grafana-aws-sdk v1.2.0/go.mod h1:bBo7qOmM3f61vO+2JxTolNUph1l2TmtzmWcU9/Im+8A= -github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0 h1:0TYrkzAc3u0HX+9GK86cGrLTUAcmQfl3/LEB3tL+SOA= -github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0/go.mod h1:H9sVh9A4yg5egMGZeh0mifxT1Q/uqwKe1LBjBJU6pN8= +github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1 h1:FFcEA01tW+SmuJIuDbHOdgUBL+d7DPrZ2N4zwzPhfGk= +github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1/go.mod h1:Oi4anANlCuTCc66jCyqIzfVbgLXFll8Wja+Y4vfANlc= github.com/grafana/grafana-cloud-migration-snapshot v1.9.0 h1:JOzchPgptwJdruYoed7x28lFDwhzs7kssResYsnC0iI= github.com/grafana/grafana-cloud-migration-snapshot v1.9.0/go.mod h1:nOHgq4Oa829qmBKA5KIXw5Ipo3rhLs0d6A8UI9Nw8Zk= github.com/grafana/grafana-google-sdk-go v0.4.2 h1:F44hQF1y6UVJhlJPi+Mz+GCJsioVgezEgPMMEQbUZRo= @@ -2285,8 +2285,8 @@ github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2z github.com/puzpuzpuz/xsync/v4 v4.2.0 h1:dlxm77dZj2c3rxq0/XNvvUKISAmovoXF4a4qM6Wvkr0= github.com/puzpuzpuz/xsync/v4 v4.2.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= -github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/redis/go-redis/v9 v9.14.0 h1:u4tNCjXOyzfgeLN+vAZaW1xUooqWDqVEsZN0U01jfAE= +github.com/redis/go-redis/v9 v9.14.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/redis/rueidis v1.0.64 h1:XqgbueDuNV3qFdVdQwAHJl1uNt90zUuAJuzqjH4cw6Y= github.com/redis/rueidis v1.0.64/go.mod h1:Lkhr2QTgcoYBhxARU7kJRO8SyVlgUuEkcJO1Y8MCluA= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= @@ -2875,8 +2875,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/go.work b/go.work index efba709a41b..6c07df8c326 100644 --- a/go.work +++ b/go.work @@ -1,4 +1,4 @@ -go 1.24.6 +go 1.25.3 // The `skip:golangci-lint` comment tag is used to exclude the package from the `golangci-lint` GitHub Action. // The module at the root of the repo (`.`) is excluded because ./pkg/... is included manually in the `golangci-lint` configuration. diff --git a/go.work.sum b/go.work.sum index 00ee74f2be3..4ad9d238375 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1059,7 +1059,6 @@ github.com/grafana/cog v0.0.37/go.mod h1:UDstzYqMdgIROmbfkHL8fB9XWQO2lnf5z+4W/eJ github.com/grafana/cog v0.0.38 h1:V7gRRn/mh7Bg1ptrCxo0bv6K0SnG9TiDZk+3Ppftn6s= github.com/grafana/cog v0.0.38/go.mod h1:UDstzYqMdgIROmbfkHL8fB9XWQO2lnf5z+4W/eJo4Dc= github.com/grafana/cog v0.0.40/go.mod h1:TDunc7TYF7EfzjwFOlC5AkMe3To/U2KqyyG3QVvrF38= -github.com/grafana/cog v0.0.41/go.mod h1:TDunc7TYF7EfzjwFOlC5AkMe3To/U2KqyyG3QVvrF38= github.com/grafana/dskit v0.0.0-20250818234656-8ff9c6532e85/go.mod h1:kImsvJ1xnmeT9Z6StK+RdEKLzlpzBsKwJbEQfmBJdFs= github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak= github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90= @@ -1225,6 +1224,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kevinmbeaulieu/eq-go v1.0.0 h1:AQgYHURDOmnVJ62jnEk0W/7yFKEn+Lv8RHN6t7mB0Zo= github.com/kevinmbeaulieu/eq-go v1.0.0/go.mod h1:G3S8ajA56gKBZm4UB9AOyoOS37JO3roToPzKNM8dtdM= +github.com/keybase/dbus v0.0.0-20220506165403-5aa21ea2c23a h1:K0EAzgzEQHW4Y5lxrmvPMltmlRDzlhLfGmots9EHUTI= +github.com/keybase/dbus v0.0.0-20220506165403-5aa21ea2c23a/go.mod h1:YPNKjjE7Ubp9dTbnWvsP3HT+hYnY6TfXzubYTBeUxc8= github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46 h1:veS9QfglfvqAw2e+eeNT/SbGySq8ajECXJ9e4fPoLhY= @@ -1337,6 +1338,7 @@ github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/mostynb/go-grpc-compression v1.2.3 h1:42/BKWMy0KEJGSdWvzqIyOZ95YcR9mLPqKctH7Uo//I= @@ -1454,6 +1456,7 @@ github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHf github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM= @@ -1983,6 +1986,7 @@ golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -1995,6 +1999,7 @@ golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/hack/go.mod b/hack/go.mod index ba694313e1f..6234b45073e 100644 --- a/hack/go.mod +++ b/hack/go.mod @@ -1,16 +1,16 @@ module github.com/grafana/grafana/hack -go 1.24.6 +go 1.25.3 require k8s.io/code-generator v0.34.1 require ( github.com/go-logr/logr v1.4.2 // indirect github.com/spf13/pflag v1.0.6 // indirect - golang.org/x/mod v0.24.0 // indirect - golang.org/x/sync v0.14.0 // indirect - golang.org/x/text v0.25.0 // indirect - golang.org/x/tools v0.33.0 // indirect + golang.org/x/mod v0.27.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.36.0 // indirect k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f // indirect k8s.io/klog/v2 v2.130.1 // indirect ) diff --git a/hack/go.sum b/hack/go.sum index 60d1ab0595e..b370b15f40f 100644 --- a/hack/go.sum +++ b/hack/go.sum @@ -4,14 +4,14 @@ 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/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= k8s.io/code-generator v0.34.1 h1:WpphT26E+j7tEgIUfFr5WfbJrktCGzB3JoJH9149xYc= k8s.io/code-generator v0.34.1/go.mod h1:DeWjekbDnJWRwpw3s0Jat87c+e0TgkxoR4ar608yqvg= k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f h1:SLb+kxmzfA87x4E4brQzB33VBbT2+x7Zq9ROIHmGn9Q= diff --git a/kinds/dashboard/dashboard_kind.cue b/kinds/dashboard/dashboard_kind.cue index 20571c8211a..13c2b4f25f6 100644 --- a/kinds/dashboard/dashboard_kind.cue +++ b/kinds/dashboard/dashboard_kind.cue @@ -360,8 +360,9 @@ lineage: schemas: [{ // `textbox`: Display a free text input field with an optional default value. // `custom`: Define the variable options manually using a comma-separated list. // `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables + // `switch`: Boolean variables rendered as a switch #VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" | - "system" | "snapshot" @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview) + "system" | "snapshot" | "switch" @cuetsy(kind="type") @grafanamaturity(NeedsExpertReview) // Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. // Continuous color interpolates a color using the percentage of a value relative to min and max. diff --git a/kinds/librarypanel/librarypanel_kind.cue b/kinds/librarypanel/librarypanel_kind.cue deleted file mode 100644 index b87ceccb6fc..00000000000 --- a/kinds/librarypanel/librarypanel_kind.cue +++ /dev/null @@ -1,63 +0,0 @@ -package kind - -import ( - "strings" - "time" -) - -name: "LibraryPanel" -maturity: "experimental" -description: "A standalone panel" - -lineage: schemas: [{ - version: [0, 0] - schema: { - spec: { - // Folder UID - folderUid?: string @grafanamaturity(ToMetadata="sys") - - // Library element UID - uid: string - - // Panel name (also saved in the model) - name: string & strings.MinRunes(1) - - // Panel description - description?: string - - // The panel type (from inside the model) - type: string & strings.MinRunes(1) - - // Dashboard version when this was saved (zero if unknown) - schemaVersion?: uint16 - - // panel version, incremented each time the dashboard is updated. - version: int64 @grafanamaturity(NeedsExpertReview) - - // TODO: should be the same panel schema defined in dashboard - // Typescript: Omit; - model: {...} - - // Object storage metadata - meta?: #LibraryElementDTOMeta @grafanamaturity(ToMetadata="sys") - } @cuetsy(kind="interface") @grafana(TSVeneer="type") - - #LibraryElementDTOMetaUser: { - id: int64 - name: string - avatarUrl: string - } @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) - - #LibraryElementDTOMeta: { - folderName: string - folderUid: string @grafanamaturity(ToMetadata="sys") - connectedDashboards: int64 - - created: string & time.Time - updated: string & time.Time - - createdBy: #LibraryElementDTOMetaUser @grafanamaturity(ToMetadata="sys") - updatedBy: #LibraryElementDTOMetaUser @grafanamaturity(ToMetadata="sys") - } @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview) - } -}] diff --git a/package.json b/package.json index 3b6c04e0c01..68aa8df691e 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,8 @@ "stats": "webpack --mode production --config scripts/webpack/webpack.prod.js --profile --json > compilation-stats.json", "storybook": "yarn workspace @grafana/ui storybook --ci", "storybook:build": "yarn workspace @grafana/ui storybook:build", - "themes-generate": "esbuild --target=es6 ./scripts/cli/generateSassVariableFiles.ts --bundle --platform=node --tsconfig=./scripts/cli/tsconfig.json | node", + "themes-schema": "typescript-json-schema ./tsconfig.json NewThemeOptions --include 'packages/grafana-data/src/themes/createTheme.ts' --out public/app/features/theme-playground/schema.generated.json", + "themes-generate": "yarn themes-schema && esbuild --target=es6 ./scripts/cli/generateSassVariableFiles.ts --bundle --platform=node --tsconfig=./scripts/cli/tsconfig.json | node", "themes:usage": "eslint . --ignore-pattern '*.test.ts*' --ignore-pattern '*.spec.ts*' --cache --plugin '@grafana' --rule '{ @grafana/theme-token-usage: \"error\" }'", "typecheck": "tsc --noEmit && yarn run packages:typecheck", "plugins:build-bundled": "echo 'bundled plugins are no longer supported'", @@ -255,6 +256,7 @@ "ts-jest": "29.4.0", "ts-node": "10.9.2", "typescript": "5.9.2", + "typescript-json-schema": "^0.65.1", "webpack": "5.101.0", "webpack-assets-manifest": "^5.1.0", "webpack-cli": "6.0.1", @@ -294,8 +296,8 @@ "@grafana/plugin-ui": "^0.10.10", "@grafana/prometheus": "workspace:*", "@grafana/runtime": "workspace:*", - "@grafana/scenes": "6.38.0", - "@grafana/scenes-react": "6.38.0", + "@grafana/scenes": "6.39.5", + "@grafana/scenes-react": "6.39.5", "@grafana/schema": "workspace:*", "@grafana/sql": "workspace:*", "@grafana/ui": "workspace:*", @@ -310,6 +312,7 @@ "@locker/near-membrane-shared-dom": "0.14.0", "@msagl/core": "^1.1.19", "@msagl/parser": "^1.1.19", + "@openfeature/ofrep-web-provider": "^0.3.3", "@openfeature/web-sdk": "^1.6.1", "@opentelemetry/api": "1.9.0", "@opentelemetry/exporter-collector": "0.25.0", diff --git a/packages/grafana-alerting/package.json b/packages/grafana-alerting/package.json index f162791a6bf..167358447d0 100644 --- a/packages/grafana-alerting/package.json +++ b/packages/grafana-alerting/package.json @@ -70,6 +70,7 @@ "@types/lodash": "^4", "@types/react": "18.3.18", "@types/react-dom": "18.3.5", + "@types/tinycolor2": "^1", "i18next": "^25.5.2", "react": "18.3.1", "react-dom": "18.3.1", @@ -85,7 +86,6 @@ "@grafana/data": ">=11.6 <= 12.x", "@grafana/runtime": ">=11.6 <= 12.x", "@grafana/ui": ">=11.6 <= 12.x", - "@reduxjs/toolkit": "^2.8.0", "msw": "^2.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" @@ -94,7 +94,9 @@ "@emotion/css": "11.13.5", "@faker-js/faker": "^9.8.0", "@grafana/i18n": "12.3.0-pre", + "@reduxjs/toolkit": "^2.9.0", "fishery": "^2.3.1", - "lodash": "^4.17.21" + "lodash": "^4.17.21", + "tinycolor2": "^1.6.0" } } diff --git a/packages/grafana-data/src/dataframe/processDataFrame.test.ts b/packages/grafana-data/src/dataframe/processDataFrame.test.ts index 72b3041d87c..5c7593e91c0 100644 --- a/packages/grafana-data/src/dataframe/processDataFrame.test.ts +++ b/packages/grafana-data/src/dataframe/processDataFrame.test.ts @@ -365,6 +365,7 @@ describe('sorted DataFrame', () => { { name: 'fourth', type: FieldType.time, values: [1, 2, 3], nanos: [10, 20, 30] }, ], }); + it('Should sort numbers', () => { const sorted = sortDataFrame(frame, 0, true); expect(sorted.length).toEqual(3); @@ -386,6 +387,17 @@ describe('sorted DataFrame', () => { expect(sorted.fields[3].values).toEqual([3, 2, 1]); expect(sorted.fields[3].nanos).toEqual([30, 20, 10]); }); + + it('Should handle arrays with empty values correctly', () => { + // Create a sparse array with empty slots (undefined values) + const values = ['502', '502', , '500', '500', '200', '404']; // Note the empty slot at index 2 + const frame = toDataFrame({ + fields: [{ name: 'status', type: FieldType.string, values }], + }); + const sorted = sortDataFrame(frame, 0, false); + + expect(sorted.fields[0].values).toEqual(['200', '404', '500', '500', '502', '502', undefined]); + }); }); describe('sorted DataFrame by nanos', () => { diff --git a/packages/grafana-data/src/dataframe/processDataFrame.ts b/packages/grafana-data/src/dataframe/processDataFrame.ts index 4a028b6b4fd..14621547725 100644 --- a/packages/grafana-data/src/dataframe/processDataFrame.ts +++ b/packages/grafana-data/src/dataframe/processDataFrame.ts @@ -429,18 +429,20 @@ export function sortDataFrame(data: DataFrame, sortIndex?: number, reverse = fal return { ...data, - fields: data.fields.map((f) => { - const newF = { - ...f, - values: f.values.map((v, i) => f.values[index[i]]), + fields: data.fields.map((field) => { + const newValues = Array.from({ length: field.values.length }, (_, i) => field.values[index[i]]); + + const newField = { + ...field, + values: newValues, }; // only add .nanos if it exists - const { nanos } = f; + const { nanos } = field; if (nanos !== undefined) { - newF.nanos = nanos.map((n, i) => nanos[index[i]]); + newField.nanos = Array.from({ length: nanos.length }, (_, i) => nanos[index[i]]); } - return newF; + return newField; }), }; } diff --git a/packages/grafana-data/src/field/displayProcessor.test.ts b/packages/grafana-data/src/field/displayProcessor.test.ts index 2cd13b48b58..b47ca53b17e 100644 --- a/packages/grafana-data/src/field/displayProcessor.test.ts +++ b/packages/grafana-data/src/field/displayProcessor.test.ts @@ -423,6 +423,11 @@ describe('Format value', () => { const processor = getDisplayProcessorFromConfig({ decimals: 5 }, FieldType.number); expect(processor(35, 2).text).toEqual('35.00000'); }); + + it('does not attempt to coerce non-numeric strings back to numbers', () => { + const processor = getDisplayProcessorFromConfig({ unit: 'dthms' }, FieldType.number); + expect(processor(35, 2).text).toEqual('00:00:35'); // we're checking that this isn't NaN. + }); }); }); diff --git a/packages/grafana-data/src/field/displayProcessor.ts b/packages/grafana-data/src/field/displayProcessor.ts index a9f0a7c2927..28c17bb604d 100644 --- a/packages/grafana-data/src/field/displayProcessor.ts +++ b/packages/grafana-data/src/field/displayProcessor.ts @@ -49,7 +49,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP const { palette } = options.theme.visualization; let unit = config.unit; - let hasDateUnit = unit && (timeFormats[unit] || unit.startsWith('time:')); + let hasDateUnit = Boolean(unit && (timeFormats[unit] || unit.startsWith('time:'))); let showMs = false; if (field.type === FieldType.time && !hasDateUnit) { @@ -152,7 +152,11 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP // this is needed because we may have determined the minimum determined `adjacentDecimals` for y tick increments based on // e.g. 'seconds' field unit (0.15s, 0.20s, 0.25s), but then formatFunc decided to return milli or nanos (150, 200, 250) // so we end up with excess precision: 150.00, 200.00, 250.00 - v.text = +v.text + ''; + // #73795 - some units, like duration formats, return text which cannot be coerced back into a number safely at this point. + const asNum = +v.text; + if (!Number.isNaN(asNum)) { + v.text = asNum + ''; + } } else { v = formatFunc(numeric, config.decimals, null, options.timeZone, showMs); } diff --git a/packages/grafana-data/src/field/fieldColor.test.ts b/packages/grafana-data/src/field/fieldColor.test.ts index bcfa9648df4..3463549c3c4 100644 --- a/packages/grafana-data/src/field/fieldColor.test.ts +++ b/packages/grafana-data/src/field/fieldColor.test.ts @@ -47,7 +47,7 @@ describe('fieldColorModeRegistry', () => { it('Palette classic with series index 1', () => { const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 1, name: 'series2' }); - expect(calcFn(70, 0, undefined)).toEqual('#FADE2A'); + expect(calcFn(70, 0, undefined)).toEqual('#F2CC0C'); }); it('Palette uses name', () => { diff --git a/packages/grafana-data/src/field/fieldDisplay.test.ts b/packages/grafana-data/src/field/fieldDisplay.test.ts index d4419310bad..718c0e54430 100644 --- a/packages/grafana-data/src/field/fieldDisplay.test.ts +++ b/packages/grafana-data/src/field/fieldDisplay.test.ts @@ -388,7 +388,7 @@ describe('FieldDisplay', () => { const result = getFieldDisplayValues(options); expect(result[0].display.color).toEqual('#73BF69'); - expect(result[1].display.color).toEqual('#FADE2A'); + expect(result[1].display.color).toEqual('#F2CC0C'); }); it('When showing all values lookup color via an override', () => { @@ -435,7 +435,7 @@ describe('FieldDisplay', () => { const result = getFieldDisplayValues(options); expect(result[0].display.color).toEqual('#AAA'); - expect(result[1].display.color).toEqual('#FADE2A'); + expect(result[1].display.color).toEqual('#F2CC0C'); }); it('Multiple other string fields', () => { diff --git a/packages/grafana-data/src/index.ts b/packages/grafana-data/src/index.ts index 269dba30b62..e058612a3c7 100644 --- a/packages/grafana-data/src/index.ts +++ b/packages/grafana-data/src/index.ts @@ -533,6 +533,7 @@ export { type QueryVariableModel, type TextBoxVariableModel, type ConstantVariableModel, + type SwitchVariableModel, type VariableWithMultiSupport, type VariableWithOptions, type DashboardProps, diff --git a/packages/grafana-data/src/internal/index.ts b/packages/grafana-data/src/internal/index.ts index 8fcb8da4e0d..e2dab753baa 100644 --- a/packages/grafana-data/src/internal/index.ts +++ b/packages/grafana-data/src/internal/index.ts @@ -93,6 +93,7 @@ export { DataTransformerID } from '../transformations/transformers/ids'; export { mergeTransformer } from '../transformations/transformers/merge'; export { getThemeById } from '../themes/registry'; +export * as experimentalThemeDefinitions from '../themes/themeDefinitions'; export { GrafanaEdition } from '../types/config'; export { SIPrefix } from '../valueFormats/symbolFormatters'; diff --git a/packages/grafana-data/src/themes/createVisualizationColors.ts b/packages/grafana-data/src/themes/createVisualizationColors.ts index 80238d2a655..fca963c9c07 100644 --- a/packages/grafana-data/src/themes/createVisualizationColors.ts +++ b/packages/grafana-data/src/themes/createVisualizationColors.ts @@ -261,7 +261,7 @@ function getClassicPalette() { return [ 'green', - 'yellow', + 'semi-dark-yellow', 'blue', 'orange', 'red', diff --git a/packages/grafana-data/src/types/config.ts b/packages/grafana-data/src/types/config.ts index 29079be8bd8..92991080976 100644 --- a/packages/grafana-data/src/types/config.ts +++ b/packages/grafana-data/src/types/config.ts @@ -1,3 +1,5 @@ +import type { InternalLoggerLevel } from '@grafana/faro-core'; + import { SystemDateFormatSettings } from '../datetime/formats'; import { MapLayerOptions } from '../geo/layer'; @@ -93,11 +95,14 @@ export interface LicenseInfo { export interface GrafanaJavascriptAgentConfig { enabled: boolean; customEndpoint: string; - errorInstrumentalizationEnabled: boolean; - consoleInstrumentalizationEnabled: boolean; - webVitalsInstrumentalizationEnabled: boolean; - tracingInstrumentalizationEnabled: boolean; apiKey: string; + internalLoggerLevel: InternalLoggerLevel; + + consoleInstrumentalizationEnabled: boolean; + performanceInstrumentalizationEnabled: boolean; + cspInstrumentalizationEnabled: boolean; + tracingInstrumentalizationEnabled: boolean; + webVitalsAttribution: boolean; } export interface UnifiedAlertingStateHistoryConfig { @@ -316,6 +321,7 @@ export interface GrafanaConfig { quickRanges?: TimeOption[]; pluginRestrictedAPIsAllowList?: Record; pluginRestrictedAPIsBlockList?: Record; + openFeatureContext: Record; // The namespace to use for kubernetes apiserver requests namespace: string; diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 632a507db07..476c60e68da 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -396,6 +396,10 @@ export interface FeatureToggles { */ dashboardUndoRedo?: boolean; /** + * Enables unlimited dashboard panel grouping + */ + unlimitedLayoutsNesting?: boolean; + /** * Enables use of the `systemPanelFilterVar` variable to filter panels in a dashboard */ panelFilterVariable?: boolean; @@ -488,11 +492,6 @@ export interface FeatureToggles { */ useMultipleScopeNodesEndpoint?: boolean; /** - * In-development feature that will allow injection of labels into prometheus queries. - * @default true - */ - promQLScope?: boolean; - /** * In-development feature that will allow injection of labels into loki queries. * @default false */ @@ -1031,6 +1030,10 @@ export interface FeatureToggles { */ kubernetesAuthzResourcePermissionApis?: boolean; /** + * Enable sync of Zanzana authorization store on AuthZ CRD mutations + */ + kubernetesAuthzZanzanaSync?: boolean; + /** * Enables create, delete, and update mutations for resources owned by IAM identity */ kubernetesAuthnMutation?: boolean; @@ -1094,6 +1097,11 @@ export interface FeatureToggles { */ enableAppChromeExtensions?: boolean; /** + * Set this to true to enable all dashboard empty state extensions registered by plugins. + * @default false + */ + enableDashboardEmptyExtensions?: boolean; + /** * Enables use of app platform API for folders * @default false */ @@ -1113,11 +1121,6 @@ export interface FeatureToggles { */ alertingNotificationHistory?: boolean; /** - * Allows decoupled core plugins to load from the Grafana CDN - * @default false - */ - pluginAssetProvider?: boolean; - /** * Enable dual reader for unified storage search */ unifiedStorageSearchDualReaderEnabled?: boolean; @@ -1163,6 +1166,10 @@ export interface FeatureToggles { */ teamFolders?: boolean; /** + * Enables Pathfinder app + */ + grafanaPathfinder?: boolean; + /** * Enables the alerting triage feature * @default false */ diff --git a/packages/grafana-data/src/types/icon.ts b/packages/grafana-data/src/types/icon.ts index b59fdeca75a..791d14c9a9a 100644 --- a/packages/grafana-data/src/types/icon.ts +++ b/packages/grafana-data/src/types/icon.ts @@ -28,6 +28,7 @@ export const availableIconsIndex = { apps: true, 'archive-alt': true, arrow: true, + 'arrow-circle-right': true, 'arrow-down': true, 'arrow-from-right': true, 'arrow-left': true, @@ -69,7 +70,10 @@ export const availableIconsIndex = { 'clipboard-alt': true, 'clock-nine': true, cloud: true, + 'cloud-database-tree': true, 'cloud-download': true, + 'cloud-exclamation': true, + 'cloud-info': true, 'cloud-provider': true, 'cloud-upload': true, 'code-branch': true, @@ -177,6 +181,7 @@ export const availableIconsIndex = { 'key-skeleton-alt': true, keyboard: true, kubernetes: true, + 'laptop-cloud': true, 'layer-group': true, 'layers-alt': true, layers: true, diff --git a/packages/grafana-data/src/types/pluginExtensions.ts b/packages/grafana-data/src/types/pluginExtensions.ts index 4707c9123dd..9a02e5fac27 100644 --- a/packages/grafana-data/src/types/pluginExtensions.ts +++ b/packages/grafana-data/src/types/pluginExtensions.ts @@ -195,6 +195,7 @@ export enum PluginExtensionPoints { AlertingRuleQueryEditor = 'grafana/alerting/alertingrule/queryeditor', CommandPalette = 'grafana/commandpalette/action', DashboardPanelMenu = 'grafana/dashboard/panel/menu', + DashboardEmpty = 'grafana/dashboard/empty', DataSourceConfig = 'grafana/datasources/config', DataSourceConfigActions = 'grafana/datasources/config/actions', DataSourceConfigErrorStatus = 'grafana/datasources/config/error-status', diff --git a/packages/grafana-data/src/types/templateVars.ts b/packages/grafana-data/src/types/templateVars.ts index 347b01b10d1..e6feea4dd3f 100644 --- a/packages/grafana-data/src/types/templateVars.ts +++ b/packages/grafana-data/src/types/templateVars.ts @@ -23,7 +23,8 @@ export type TypedVariableModel = | UserVariableModel | OrgVariableModel | DashboardVariableModel - | SnapshotVariableModel; + | SnapshotVariableModel + | SwitchVariableModel; export enum VariableRefresh { never, // removed from the UI @@ -130,6 +131,10 @@ export interface ConstantVariableModel extends VariableWithOptions { type: 'constant'; } +export interface SwitchVariableModel extends VariableWithOptions { + type: 'switch'; +} + export interface VariableWithMultiSupport extends VariableWithOptions { multi: boolean; includeAll: boolean; diff --git a/packages/grafana-e2e-selectors/src/selectors/components.ts b/packages/grafana-e2e-selectors/src/selectors/components.ts index 05b0f553578..9dc1afc8a89 100644 --- a/packages/grafana-e2e-selectors/src/selectors/components.ts +++ b/packages/grafana-e2e-selectors/src/selectors/components.ts @@ -670,6 +670,11 @@ export const versionedComponents = { '12.1.0': 'data-testid fill screen switch', }, }, + TabsLayout: { + titleInput: { + '12.2.0': 'data-testid tab title input', + }, + }, }, }, PanelInspector: { diff --git a/packages/grafana-e2e-selectors/src/selectors/pages.ts b/packages/grafana-e2e-selectors/src/selectors/pages.ts index 581a8294d10..7716ccc1436 100644 --- a/packages/grafana-e2e-selectors/src/selectors/pages.ts +++ b/packages/grafana-e2e-selectors/src/selectors/pages.ts @@ -500,24 +500,9 @@ export const versionedPages = { queryOptionsQueryInput: { '10.4.0': 'data-testid Variable editor Form Default Variable Query Editor textarea', }, - queryOptionsStaticOptionsRow: { - [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options row', - }, queryOptionsStaticOptionsToggle: { [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options toggle', }, - queryOptionsStaticOptionsLabelInput: { - [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options Label input', - }, - queryOptionsStaticOptionsValueInput: { - [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options Value input', - }, - queryOptionsStaticOptionsDeleteButton: { - [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options Delete button', - }, - queryOptionsStaticOptionsAddButton: { - [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options Add button', - }, queryOptionsStaticOptionsOrderDropdown: { [MIN_GRAFANA_VERSION]: 'Variable editor Form Query Static Options Order dropdown', }, @@ -559,6 +544,12 @@ export const versionedPages = { customValueInput: { [MIN_GRAFANA_VERSION]: 'data-testid custom-variable-input', }, + optionsOpenButton: { + [MIN_GRAFANA_VERSION]: 'data-testid custom-variable-options-open-button', + }, + closeButton: { + [MIN_GRAFANA_VERSION]: 'data-testid custom-variable-close-button', + }, }, IntervalVariable: { intervalsValueInput: { @@ -596,6 +587,37 @@ export const versionedPages = { '11.0.0': 'data-testid ad-hoc filters variable mode toggle', }, }, + SwitchVariable: { + valuePairTypeSelect: { + ['12.3.0']: 'data-testid switch variable value pair type select', + }, + enabledValueInput: { + ['12.3.0']: 'data-testid switch variable enabled value input', + }, + disabledValueInput: { + ['12.3.0']: 'data-testid switch variable disabled value input', + }, + }, + StaticOptionsEditor: { + addButton: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Add button', + }, + labelInput: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Label input', + }, + valueInput: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Value input', + }, + moveButton: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Move button', + }, + deleteButton: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Delete button', + }, + row: { + [MIN_GRAFANA_VERSION]: 'data-testid Variable editor Form Static Options Row', + }, + }, }, }, }, diff --git a/packages/grafana-prometheus/src/components/monaco-query-field/promql.ts b/packages/grafana-prometheus/src/components/monaco-query-field/promql.ts index 4a850789c72..d144b130208 100644 --- a/packages/grafana-prometheus/src/components/monaco-query-field/promql.ts +++ b/packages/grafana-prometheus/src/components/monaco-query-field/promql.ts @@ -74,6 +74,7 @@ const functions = [ 'absent', 'ceil', 'changes', + 'clamp', 'clamp_max', 'clamp_min', 'day_of_month', diff --git a/packages/grafana-prometheus/src/datasource.test.ts b/packages/grafana-prometheus/src/datasource.test.ts index bc30a4befff..ac5e2f59623 100644 --- a/packages/grafana-prometheus/src/datasource.test.ts +++ b/packages/grafana-prometheus/src/datasource.test.ts @@ -664,10 +664,6 @@ describe('PrometheusDatasource', () => { }); describe('interpolateVariablesInQueries', () => { - afterEach(() => { - config.featureToggles.promQLScope = undefined; - }); - it('should call replace function 3 times', () => { const query: PromQuery = { expr: 'test{job="testjob"}', @@ -695,17 +691,32 @@ describe('PrometheusDatasource', () => { expect(ds.enhanceExprWithAdHocFilters).toHaveBeenCalled(); }); - it('should not apply adhoc filters when promQLScope is enabled', () => { - config.featureToggles.promQLScope = true; + it('should not apply adhoc filters when scopes with filters are present', () => { ds.enhanceExprWithAdHocFilters = jest.fn(); ds.generateScopeFilters = jest.fn(); - const queries = [ + // Fix: Use correct types for scopes and filters + const typedScopes = [ + { + name: 'test-scope', + title: 'Test Scope', + type: 'test', + filters: [ + { + key: 'bar', + operator: 'equals' as const, + value: 'baz', + }, + ], + }, + ]; + const typedQueries: PromQuery[] = [ { refId: 'A', expr: 'rate({bar="baz", job="foo"} [5m]', + scopes: typedScopes, }, ]; - ds.interpolateVariablesInQueries(queries, {}); + ds.interpolateVariablesInQueries(typedQueries, {}); expect(ds.enhanceExprWithAdHocFilters).not.toHaveBeenCalled(); expect(ds.generateScopeFilters).toHaveBeenCalled(); }); @@ -716,10 +727,6 @@ describe('PrometheusDatasource', () => { replaceMock.mockImplementation((a: string, ...rest: unknown[]) => a); }); - afterEach(() => { - config.featureToggles.promQLScope = false; - }); - it('should call replace function for legendFormat', () => { const query = { expr: 'test{job="bar"}', @@ -783,7 +790,21 @@ describe('PrometheusDatasource', () => { }); it('should generate scope filters and **not** apply ad-hoc filters to expr', () => { - config.featureToggles.promQLScope = true; + const scopes = [ + { + name: 'test-scope', + title: 'Test Scope', + type: 'test', + filters: [ + { + key: 'bar', + operator: 'equals' as const, + value: 'baz', + }, + ], + }, + ]; + replaceMock.mockImplementation((a: string) => a); const filters = [ { @@ -801,17 +822,18 @@ describe('PrometheusDatasource', () => { const query = { expr: 'test{job="bar"}', refId: 'A', + scopes: scopes, }; const expectedScopeFilters: ScopeSpecFilter[] = [ { key: 'k1', - operator: 'equals', + operator: 'equals' as const, value: 'v1', }, { key: 'k2', - operator: 'not-equals', + operator: 'not-equals' as const, value: 'v2', }, ]; @@ -891,13 +913,27 @@ describe('PrometheusDatasource', () => { }); it('should replace variables in adhoc filters on backend when promQLScope is enabled', () => { - config.featureToggles.promQLScope = true; + const scopes = [ + { + name: 'test-scope', + title: 'Test Scope', + type: 'test', + filters: [ + { + key: 'bar', + operator: 'equals' as const, + value: 'baz', + }, + ], + }, + ]; const searchPattern = /\$A/g; replaceMock.mockImplementation((a: string) => a?.replace(searchPattern, '99') ?? a); const query = { expr: 'test', refId: 'A', + scopes: scopes, }; const filters = [ { @@ -906,6 +942,7 @@ describe('PrometheusDatasource', () => { value: '$A', }, ]; + const result = ds.applyTemplateVariables(query, {}, filters); expect(result).toMatchObject({ expr: 'test', diff --git a/packages/grafana-prometheus/src/datasource.ts b/packages/grafana-prometheus/src/datasource.ts index 9755325ff78..4f2beb195d1 100644 --- a/packages/grafana-prometheus/src/datasource.ts +++ b/packages/grafana-prometheus/src/datasource.ts @@ -430,7 +430,7 @@ export class PrometheusDatasource utcOffsetSec: utcOffset * 60, }; - if (config.featureToggles.promQLScope) { + if (request.scopes) { processedTarget.scopes = (request.scopes ?? []).map((scope) => ({ name: scope.metadata.name, ...scope.spec, @@ -543,7 +543,7 @@ export class PrometheusDatasource options.timeRange = getDefaultTimeRange(); } - if (config.featureToggles.promQLScope && (options?.scopes?.length ?? 0) > 0) { + if ((options?.scopes?.length ?? 0) > 0) { const suggestions = await this.languageProvider.fetchSuggestions( options.timeRange, options.queries, @@ -574,7 +574,7 @@ export class PrometheusDatasource } const requestId = `[${this.uid}][${options.key}]`; - if (config.featureToggles.promQLScope && (options?.scopes?.length ?? 0) > 0) { + if ((options?.scopes?.length ?? 0) > 0) { return ( await this.languageProvider.fetchSuggestions( options.timeRange, @@ -609,7 +609,7 @@ export class PrometheusDatasource scopedVars, this.interpolateExploreMetrics(query.fromExploreMetrics) ); - const replacedInterpolatedQuery = config.featureToggles.promQLScope + const replacedInterpolatedQuery = targetHasScopes(query) ? interpolatedQuery : this.templateSrv.replace( this.enhanceExprWithAdHocFilters(filters, interpolatedQuery), @@ -619,7 +619,7 @@ export class PrometheusDatasource const expandedQuery = { ...query, - ...(config.featureToggles.promQLScope ? { adhocFilters: this.generateScopeFilters(filters) } : {}), + ...(query.scopes && query.scopes.length > 0 ? { adhocFilters: this.generateScopeFilters(filters) } : {}), datasource: this.getRef(), expr: replacedInterpolatedQuery, interval: this.templateSrv.replace(query.interval, scopedVars), @@ -791,13 +791,13 @@ export class PrometheusDatasource // Apply ad-hoc filters // When ad-hoc filters are applied, we replace again the variables in case the ad-hoc filters also reference a variable - const exprWithAdhoc = config.featureToggles.promQLScope + const exprWithAdhoc = targetHasScopes(target) ? expr : this.templateSrv.replace(this.enhanceExprWithAdHocFilters(filters, expr), variables, this.interpolateQueryExpr); return { ...target, - ...(config.featureToggles.promQLScope ? { adhocFilters: this.generateScopeFilters(filters) } : {}), + ...(targetHasScopes(target) ? { adhocFilters: this.generateScopeFilters(filters) } : {}), expr: exprWithAdhoc, interval: this.templateSrv.replace(target.interval, variables), legendFormat: this.templateSrv.replace(target.legendFormat, variables), @@ -862,6 +862,10 @@ export class PrometheusDatasource } } +function targetHasScopes(target: PromQuery): boolean { + return !!(target.scopes && target.scopes.length > 0); +} + export function extractRuleMappingFromGroups(groups: RawRecordingRules[]): RuleQueryMapping { return groups.reduce( (mapping, group) => diff --git a/packages/grafana-prometheus/src/locales/cs-CZ/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/cs-CZ/grafana-prometheus.json index 095fdb99b4a..7afae00fd13 100644 --- a/packages/grafana-prometheus/src/locales/cs-CZ/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/cs-CZ/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "Ve zdroji dat nebyly nalezeny žádné metriky.", "name": "Název", "type": "Typ" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "Analýza dotazu je nejednoznačná." - } } } } diff --git a/packages/grafana-prometheus/src/locales/de-DE/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/de-DE/grafana-prometheus.json index 2f872d72e6c..5b95ca6bdf4 100644 --- a/packages/grafana-prometheus/src/locales/de-DE/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/de-DE/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "Es wurden keine Metriken in der Datenquelle gefunden.", "name": "Name", "type": "Typ" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "Das Abfrage-Parsing ist unklar." - } } } } diff --git a/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json index 34fb4482a0d..58c601d9514 100644 --- a/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "There are no metrics found in the data source.", "name": "Name", "type": "Type" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "Query parsing is ambiguous." - } } } } diff --git a/packages/grafana-prometheus/src/locales/es-ES/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/es-ES/grafana-prometheus.json index 940880addf9..8b0b9d4c5d1 100644 --- a/packages/grafana-prometheus/src/locales/es-ES/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/es-ES/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "No se han encontrado métricas en la fuente de datos.", "name": "Nombre", "type": "Tipo" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "El análisis de la consulta es ambiguo." - } } } } diff --git a/packages/grafana-prometheus/src/locales/fr-FR/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/fr-FR/grafana-prometheus.json index f6c73691099..2ad85dcac95 100644 --- a/packages/grafana-prometheus/src/locales/fr-FR/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/fr-FR/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "Aucune métrique trouvée dans la source de données.", "name": "Nom", "type": "Type" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "L’analyse de la requête est ambiguë." - } } } } diff --git a/packages/grafana-prometheus/src/locales/hu-HU/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/hu-HU/grafana-prometheus.json index ef0f4990ee2..5a8d1906a38 100644 --- a/packages/grafana-prometheus/src/locales/hu-HU/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/hu-HU/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "Nem található metrika az adatforrásban.", "name": "Név", "type": "Típus" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "A lekérdezés elemzése nem egyértelmű." - } } } } diff --git a/packages/grafana-prometheus/src/locales/id-ID/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/id-ID/grafana-prometheus.json index c69be5dfd39..cf37adca5c0 100644 --- a/packages/grafana-prometheus/src/locales/id-ID/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/id-ID/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "Tidak ada metrik yang ditemukan di sumber data.", "name": "Nama", "type": "Jenis" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "Penguraian kueri mengalami masalah ketidakjelasan." - } } } } diff --git a/packages/grafana-prometheus/src/locales/it-IT/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/it-IT/grafana-prometheus.json index 0d852e162e3..314ac50298e 100644 --- a/packages/grafana-prometheus/src/locales/it-IT/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/it-IT/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "Non sono state trovate metriche nell'origine dati.", "name": "Nome", "type": "Tipo" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "L'analisi della query è ambigua." - } } } } diff --git a/packages/grafana-prometheus/src/locales/ja-JP/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/ja-JP/grafana-prometheus.json index 4f81b62c665..9efa150bca3 100644 --- a/packages/grafana-prometheus/src/locales/ja-JP/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/ja-JP/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "データソース内にメトリックが見つかりません。", "name": "名前", "type": "種類" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "クエリ解析が曖昧です。" - } } } } diff --git a/packages/grafana-prometheus/src/locales/ko-KR/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/ko-KR/grafana-prometheus.json index 299e57788c0..857372ed06e 100644 --- a/packages/grafana-prometheus/src/locales/ko-KR/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/ko-KR/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "데이터 소스에서 메트릭을 찾을 수 없습니다.", "name": "이름", "type": "유형" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "쿼리 구문 분석이 모호합니다." - } } } } diff --git a/packages/grafana-prometheus/src/locales/nl-NL/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/nl-NL/grafana-prometheus.json index ff2f4cca925..991614b0524 100644 --- a/packages/grafana-prometheus/src/locales/nl-NL/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/nl-NL/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "Er zijn geen statistieken gevonden in de gegevensbron.", "name": "Naam", "type": "Type" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "Query-parsing is dubbelzinnig." - } } } } diff --git a/packages/grafana-prometheus/src/locales/pl-PL/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/pl-PL/grafana-prometheus.json index 9c48c09d005..3c949a12c2f 100644 --- a/packages/grafana-prometheus/src/locales/pl-PL/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/pl-PL/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "W źródle danych nie znaleziono żadnych metryk.", "name": "Imię", "type": "Typ" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "Analiza zapytania jest niejednoznaczna." - } } } } diff --git a/packages/grafana-prometheus/src/locales/pt-BR/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/pt-BR/grafana-prometheus.json index 75f2e20ec65..74d47a5342b 100644 --- a/packages/grafana-prometheus/src/locales/pt-BR/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/pt-BR/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "Não há métricas encontradas na fonte de dados.", "name": "Nome", "type": "Tipo" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "A análise da consulta é ambígua." - } } } } diff --git a/packages/grafana-prometheus/src/locales/pt-PT/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/pt-PT/grafana-prometheus.json index 2a84aa41ced..0c56b656f66 100644 --- a/packages/grafana-prometheus/src/locales/pt-PT/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/pt-PT/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "Não foram encontradas métricas na origem de dados.", "name": "Nome", "type": "Tipo" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "A análise da consulta é ambígua." - } } } } diff --git a/packages/grafana-prometheus/src/locales/ru-RU/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/ru-RU/grafana-prometheus.json index c1608d71796..da73342e2e9 100644 --- a/packages/grafana-prometheus/src/locales/ru-RU/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/ru-RU/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "В источнике данных нет метрик.", "name": "Имя", "type": "Тип" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "Неоднозначные результаты анализа запроса." - } } } } diff --git a/packages/grafana-prometheus/src/locales/sv-SE/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/sv-SE/grafana-prometheus.json index 63d795e2359..e41b22075dd 100644 --- a/packages/grafana-prometheus/src/locales/sv-SE/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/sv-SE/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "Inga mätvärden hittades i datakällan.", "name": "Namn", "type": "Typ" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "Tolkningen av frågan är tvetydig." - } } } } diff --git a/packages/grafana-prometheus/src/locales/tr-TR/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/tr-TR/grafana-prometheus.json index e394fae0607..af4dea95932 100644 --- a/packages/grafana-prometheus/src/locales/tr-TR/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/tr-TR/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "Veri kaynağında metrik bulunamadı.", "name": "Ad", "type": "Tür" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "Sorgu ayrıştırma belirsiz." - } } } } diff --git a/packages/grafana-prometheus/src/locales/zh-Hans/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/zh-Hans/grafana-prometheus.json index 57a1f49d2ae..890add6aa2f 100644 --- a/packages/grafana-prometheus/src/locales/zh-Hans/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/zh-Hans/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "数据源中未找到指标。", "name": "名称", "type": "类型" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "查询解析不明确。" - } } } } diff --git a/packages/grafana-prometheus/src/locales/zh-Hant/grafana-prometheus.json b/packages/grafana-prometheus/src/locales/zh-Hant/grafana-prometheus.json index 8001cdafa8d..0f4fd89eaa2 100644 --- a/packages/grafana-prometheus/src/locales/zh-Hant/grafana-prometheus.json +++ b/packages/grafana-prometheus/src/locales/zh-Hant/grafana-prometheus.json @@ -486,11 +486,6 @@ "message-no-metrics-found": "在資料來源中未找到任何指標。", "name": "名稱(名字)", "type": "類型" - }, - "update-function-args": { - "text": { - "query-parsing-is-ambiguous": "查詢剖析不明確。" - } } } } diff --git a/packages/grafana-prometheus/src/querybuilder/parsing.test.ts b/packages/grafana-prometheus/src/querybuilder/parsing.test.ts index 53db5b12b95..b3da120e8f0 100644 --- a/packages/grafana-prometheus/src/querybuilder/parsing.test.ts +++ b/packages/grafana-prometheus/src/querybuilder/parsing.test.ts @@ -200,49 +200,13 @@ describe('buildVisualQueryFromString', () => { }); describe('nested binary operation errors in visual query editor', () => { - // Visual query builder does not currently have support for nested binary operations, for now we should throw an error in the UI letting users know that their query will be misinterpreted - it('throws error when visual query parse is ambiguous', () => { + it('does not throw error when visual query contains binary ops in function argument', () => { expect( buildVisualQueryFromString('topk(5, node_arp_entries / node_arp_entries{cluster="dev-eu-west-2"})') ).toMatchObject({ - errors: [ - { - from: 8, - text: 'Query parsing is ambiguous.', - to: 68, - }, - ], + errors: [], }); }); - - it('throws error when visual query parse with aggregation is ambiguous (scalar)', () => { - expect(buildVisualQueryFromString('topk(5, 1 / 2)')).toMatchObject({ - errors: [ - { - from: 8, - text: 'Query parsing is ambiguous.', - to: 13, - }, - ], - }); - }); - - it('throws error when visual query parse with functionCall is ambiguous', () => { - expect( - buildVisualQueryFromString( - 'clamp_min(sum by(cluster)(rate(X{le="2.5"}[5m]))+sum by (cluster) (rate(X{le="5"}[5m])), 0.001)' - ) - ).toMatchObject({ - errors: [ - { - from: 10, - text: 'Query parsing is ambiguous.', - to: 87, - }, - ], - }); - }); - it('does not throw error when visual query parse is unambiguous', () => { expect( buildVisualQueryFromString('topk(5, node_arp_entries) / node_arp_entries{cluster="dev-eu-west-2"}') @@ -967,6 +931,29 @@ describe('buildVisualQueryFromString', () => { }) ); }); + + it('parses query with functions and binary operations', () => { + expect( + buildVisualQueryFromString( + 'clamp(sum(rate(loki_distributor_bytes_received_total{cluster="loki", tenant="kubernetes"}[5m])) / 1024 / 55, 5, 30)' + ) + ).toEqual( + noErrors({ + metric: 'loki_distributor_bytes_received_total', + labels: [ + { label: 'cluster', op: '=', value: 'loki' }, + { label: 'tenant', op: '=', value: 'kubernetes' }, + ], + operations: [ + { id: 'rate', params: ['5m'] }, + { id: 'sum', params: [] }, + { id: '__divide_by', params: [1024] }, + { id: '__divide_by', params: [55] }, + { id: 'clamp', params: [5, 30] }, + ], + }) + ); + }); }); function noErrors(query: PromVisualQuery) { diff --git a/packages/grafana-prometheus/src/querybuilder/parsing.ts b/packages/grafana-prometheus/src/querybuilder/parsing.ts index 9a517b53bd2..3b7c372a545 100644 --- a/packages/grafana-prometheus/src/querybuilder/parsing.ts +++ b/packages/grafana-prometheus/src/querybuilder/parsing.ts @@ -34,6 +34,7 @@ import { getAllByType, getLeftMostChild, getString, + isFunctionOrAggregation, makeBinOp, makeError, replaceBuiltInVariable, @@ -330,24 +331,6 @@ function updateFunctionArgs(expr: string, node: SyntaxNode | null, context: Cont let child = node.firstChild; while (child) { - let binaryExpressionWithinFunctionArgs: SyntaxNode | null; - if (child.type.id === BinaryExpr) { - binaryExpressionWithinFunctionArgs = child; - } else { - binaryExpressionWithinFunctionArgs = child.getChild(BinaryExpr); - } - - if (binaryExpressionWithinFunctionArgs) { - context.errors.push({ - text: t( - 'grafana-prometheus.querybuilder.update-function-args.text.query-parsing-is-ambiguous', - 'Query parsing is ambiguous.' - ), - from: binaryExpressionWithinFunctionArgs.from, - to: binaryExpressionWithinFunctionArgs.to, - }); - } - updateFunctionArgs(expr, child, context, op); child = child.nextSibling; } @@ -392,7 +375,7 @@ function updateFunctionArgs(expr: string, node: SyntaxNode | null, context: Cont * @param node * @param context */ -function handleBinary(expr: string, node: SyntaxNode, context: Context) { +function handleBinary(expr: string, node: SyntaxNode, context: Context, idx = 0) { const visQuery = context.query; const left = node.firstChild!; const op = getString(expr, left.nextSibling); @@ -407,6 +390,16 @@ function handleBinary(expr: string, node: SyntaxNode, context: Context) { const rightBinary = right.type.id === BinaryExpr; + // binary operations that are part of a function argument do not get processed and added to the query until the end, this index helps keep track + // of where to add the operation in the list rather than just appending it to the end. If the binary operation is just part of a nested binary exp, + // we append at the end + const parent = node.parent; + const child = node.firstChild; + const shouldOffsetTail = + parent && !parent.type.isTop && (isFunctionOrAggregation(parent) || (child && isFunctionOrAggregation(child))); + if (shouldOffsetTail) { + idx += 1; + } if (leftNumber) { // TODO: this should be already handled in case parent is binary expression as it has to be added to parent // if query starts with a number that isn't handled now. @@ -416,14 +409,18 @@ function handleBinary(expr: string, node: SyntaxNode, context: Context) { handleExpression(expr, left, context); } + // in the case we have an expression like func(...) / 2 or func(...) + 5, the binary expression will be at the top of the tree + // in which case, the idx will be 0. In this case it means that the binary operation must be added to the end of the array, and + + const newIdx = idx === 0 ? visQuery.operations.length : -idx; if (rightNumber) { - visQuery.operations.push(makeBinOp(opDef, expr, right, !!binModifier?.isBool)); + visQuery.operations.splice(newIdx, 0, makeBinOp(opDef, expr, right, !!binModifier?.isBool)); } else if (rightBinary) { // Due to the way binary ops are parsed we can get a binary operation on the right that starts with a number which // is a factor for a current binary operation. So we have to add it as an operation now. const leftMostChild = getLeftMostChild(right); if (leftMostChild?.type.id === NumberDurationLiteral) { - visQuery.operations.push(makeBinOp(opDef, expr, leftMostChild, !!binModifier?.isBool)); + visQuery.operations.splice(newIdx, 0, makeBinOp(opDef, expr, leftMostChild, !!binModifier?.isBool)); } // If we added the first number literal as operation here we still can continue and handle the rest as the first diff --git a/packages/grafana-prometheus/src/querybuilder/parsingUtils.test.ts b/packages/grafana-prometheus/src/querybuilder/parsingUtils.test.ts index c96a7877e8f..fa58edd28bc 100644 --- a/packages/grafana-prometheus/src/querybuilder/parsingUtils.test.ts +++ b/packages/grafana-prometheus/src/querybuilder/parsingUtils.test.ts @@ -4,6 +4,7 @@ import { parser } from '@prometheus-io/lezer-promql'; import { getLeftMostChild, getString, + isFunctionOrAggregation, replaceBuiltInVariable, replaceVariables, returnBuiltInVariable, @@ -123,4 +124,17 @@ describe('builtInTimeVariables', () => { expect(actual2).toBe(testCase.expr); }); }); + + describe('isFunctionOrAggregation', () => { + it('should identify function and aggregation nodes', () => { + const tree = parser.parse('clamp(sum(foo[5m]))'); + const root = tree.topNode; + const functionNode = root.firstChild!.lastChild; // clamp + const aggregationNode = root.firstChild!.lastChild!.firstChild; // sum + + expect(isFunctionOrAggregation(functionNode!)).toBe(true); + expect(isFunctionOrAggregation(aggregationNode!)).toBe(true); + expect(isFunctionOrAggregation(root)).toBe(false); + }); + }); }); diff --git a/packages/grafana-prometheus/src/querybuilder/parsingUtils.ts b/packages/grafana-prometheus/src/querybuilder/parsingUtils.ts index 3017bc33526..50d3a2ce725 100644 --- a/packages/grafana-prometheus/src/querybuilder/parsingUtils.ts +++ b/packages/grafana-prometheus/src/querybuilder/parsingUtils.ts @@ -1,5 +1,6 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/parsingUtils.ts import { SyntaxNode, TreeCursor } from '@lezer/common'; +import { AggregateExpr, FunctionCallBody } from '@prometheus-io/lezer-promql'; import { QueryBuilderOperation, QueryBuilderOperationParamValue } from './shared/types'; @@ -194,3 +195,7 @@ export function replaceBuiltInVariable(expr: string): string { export function returnBuiltInVariable(expr: string): string { return expr.replace(builtInReplacementRegex, (match) => replacementToVariable[match]); } + +export function isFunctionOrAggregation(node: SyntaxNode): boolean { + return node.type.id === AggregateExpr || node.type.id === FunctionCallBody; +} diff --git a/packages/grafana-runtime/src/config.ts b/packages/grafana-runtime/src/config.ts index 2576de932b2..8fc1fb68218 100644 --- a/packages/grafana-runtime/src/config.ts +++ b/packages/grafana-runtime/src/config.ts @@ -153,13 +153,14 @@ export class GrafanaBootConfig { dateFormats?: SystemDateFormatSettings; grafanaJavascriptAgent = { enabled: false, - customEndpoint: '', apiKey: '', - allInstrumentationsEnabled: false, - errorInstrumentalizationEnabled: true, + customEndpoint: '', consoleInstrumentalizationEnabled: false, - webVitalsInstrumentalizationEnabled: false, + performanceInstrumentalizationEnabled: false, + cspInstrumentalizationEnabled: false, tracingInstrumentalizationEnabled: false, + webVitalsAttribution: false, + internalLoggerLevel: 0, }; pluginCatalogURL = 'https://grafana.com/grafana/plugins/'; pluginAdminEnabled = true; @@ -259,6 +260,8 @@ export class GrafanaBootConfig { listDashboardScopesEndpoint = ''; listScopesEndpoint = ''; + openFeatureContext: Record = {}; + constructor( options: BootData['settings'] & { bootData: BootData; diff --git a/packages/grafana-runtime/src/internal/openFeature/index.ts b/packages/grafana-runtime/src/internal/openFeature/index.ts index 891eefe7958..57e35516e30 100644 --- a/packages/grafana-runtime/src/internal/openFeature/index.ts +++ b/packages/grafana-runtime/src/internal/openFeature/index.ts @@ -24,7 +24,7 @@ export async function initOpenFeature() { await OpenFeature.setProviderAndWait(ofProvider, { targetingKey: config.namespace, - namespace: config.namespace, + ...config.openFeatureContext, }); } diff --git a/packages/grafana-runtime/src/services/index.ts b/packages/grafana-runtime/src/services/index.ts index e9d2eeac852..42ef1c3f655 100644 --- a/packages/grafana-runtime/src/services/index.ts +++ b/packages/grafana-runtime/src/services/index.ts @@ -30,6 +30,7 @@ export { type UsePluginFunctionsOptions, type UsePluginFunctionsResult, } from './pluginExtensions/usePluginFunctions'; +export { setHelpNavItemHook, useHelpNavItem, type UseHelpNavItem } from './navigation/useHelpNavItem'; export { getObservablePluginLinks } from './pluginExtensions/getObservablePluginLinks'; export { getObservablePluginComponents } from './pluginExtensions/getObservablePluginComponents'; export { diff --git a/packages/grafana-runtime/src/services/navigation/useHelpNavItem.ts b/packages/grafana-runtime/src/services/navigation/useHelpNavItem.ts new file mode 100644 index 00000000000..cb39aee2c0e --- /dev/null +++ b/packages/grafana-runtime/src/services/navigation/useHelpNavItem.ts @@ -0,0 +1,20 @@ +import { NavModelItem } from '@grafana/data'; + +export type UseHelpNavItem = () => NavModelItem | undefined; + +let singleton: UseHelpNavItem | undefined; + +export function setHelpNavItemHook(hook: UseHelpNavItem): void { + // We allow overriding the registry in tests + if (singleton && process.env.NODE_ENV !== 'test') { + throw new Error('setHelpNavItemHook() function should only be called once, when Grafana is starting.'); + } + singleton = hook; +} + +export function useHelpNavItem(): NavModelItem | undefined { + if (!singleton) { + throw new Error('useHelpNavItem() can only be used after the Grafana instance has started.'); + } + return singleton(); +} diff --git a/packages/grafana-schema/src/index.gen.ts b/packages/grafana-schema/src/index.gen.ts index 5646058691b..87f2a3fd960 100644 --- a/packages/grafana-schema/src/index.gen.ts +++ b/packages/grafana-schema/src/index.gen.ts @@ -104,20 +104,3 @@ export { defaultFieldConfig, defaultRowPanel } from './veneer/dashboard.types'; - -// Raw generated types from LibraryPanel kind. -export type { - LibraryElementDTOMetaUser, - LibraryElementDTOMeta -} from './raw/librarypanel/x/librarypanel_types.gen'; - -// The following exported declarations correspond to types in the librarypanel@0.0 kind's -// schema with attribute @grafana(TSVeneer="type"). -// -// The handwritten file for these type and default veneers is expected to be at -// packages/grafana-schema/src/veneer/librarypanel.types.ts. -// This re-export declaration enforces that the handwritten veneer file exists, -// and exports all the symbols in the list. -// -// TODO generate code such that tsc enforces type compatibility between raw and veneer decls -export type { LibraryPanel } from './veneer/librarypanel.types'; diff --git a/packages/grafana-schema/src/index.ts b/packages/grafana-schema/src/index.ts index 505aded6a29..e7588ef48a1 100644 --- a/packages/grafana-schema/src/index.ts +++ b/packages/grafana-schema/src/index.ts @@ -4,4 +4,5 @@ * @packageDocumentation */ export * from './veneer/common.types'; +export * from './veneer/librarypanel.types'; export * from './index.gen'; diff --git a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts index d158d884aa0..b370fc6206c 100644 --- a/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts +++ b/packages/grafana-schema/src/raw/dashboard/x/dashboard_types.gen.ts @@ -454,8 +454,9 @@ export const defaultAction: Partial = { * `textbox`: Display a free text input field with an optional default value. * `custom`: Define the variable options manually using a comma-separated list. * `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables + * `switch`: Boolean variables rendered as a switch */ -export type VariableType = ('query' | 'adhoc' | 'groupby' | 'constant' | 'datasource' | 'interval' | 'textbox' | 'custom' | 'system' | 'snapshot'); +export type VariableType = ('query' | 'adhoc' | 'groupby' | 'constant' | 'datasource' | 'interval' | 'textbox' | 'custom' | 'system' | 'snapshot' | 'switch'); /** * Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value. diff --git a/packages/grafana-schema/src/raw/librarypanel/x/librarypanel_types.gen.ts b/packages/grafana-schema/src/raw/librarypanel/x/librarypanel_types.gen.ts deleted file mode 100644 index 97cba2a5c5e..00000000000 --- a/packages/grafana-schema/src/raw/librarypanel/x/librarypanel_types.gen.ts +++ /dev/null @@ -1,65 +0,0 @@ -// Code generated - EDITING IS FUTILE. DO NOT EDIT. -// -// Generated by: -// kinds/gen.go -// Using jennies: -// TSTypesJenny -// LatestMajorsOrXJenny -// -// Run 'make gen-cue' from repository root to regenerate. - -export interface LibraryElementDTOMetaUser { - avatarUrl: string; - id: number; - name: string; -} - -export interface LibraryElementDTOMeta { - connectedDashboards: number; - created: string; - createdBy: LibraryElementDTOMetaUser; - folderName: string; - folderUid: string; - updated: string; - updatedBy: LibraryElementDTOMetaUser; -} - -export interface LibraryPanel { - /** - * Panel description - */ - description?: string; - /** - * Folder UID - */ - folderUid?: string; - /** - * Object storage metadata - */ - meta?: LibraryElementDTOMeta; - /** - * TODO: should be the same panel schema defined in dashboard - * Typescript: Omit; - */ - model: Record; - /** - * Panel name (also saved in the model) - */ - name: string; - /** - * Dashboard version when this was saved (zero if unknown) - */ - schemaVersion?: number; - /** - * The panel type (from inside the model) - */ - type: string; - /** - * Library element UID - */ - uid: string; - /** - * panel version, incremented each time the dashboard is updated. - */ - version: number; -} diff --git a/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts b/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts index 91bf7909fd3..978b71f39b3 100644 --- a/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v0alpha1/types.status.gen.ts @@ -12,6 +12,8 @@ export interface ConversionStatus { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. storedVersion?: string; + // The original value map[string]any + source?: any; } export const defaultConversionStatus = (): ConversionStatus => ({ diff --git a/packages/grafana-schema/src/schema/dashboard/v1beta1/types.status.gen.ts b/packages/grafana-schema/src/schema/dashboard/v1beta1/types.status.gen.ts index 91bf7909fd3..978b71f39b3 100644 --- a/packages/grafana-schema/src/schema/dashboard/v1beta1/types.status.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v1beta1/types.status.gen.ts @@ -12,6 +12,8 @@ export interface ConversionStatus { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. storedVersion?: string; + // The original value map[string]any + source?: any; } export const defaultConversionStatus = (): ConversionStatus => ({ diff --git a/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts index 91bf7909fd3..978b71f39b3 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2alpha1/types.status.gen.ts @@ -12,6 +12,8 @@ export interface ConversionStatus { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. storedVersion?: string; + // The original value map[string]any + source?: any; } export const defaultConversionStatus = (): ConversionStatus => ({ diff --git a/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts index 40c2d212d31..aabcbcec235 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2beta1/types.spec.gen.ts @@ -1052,7 +1052,7 @@ export const defaultTimeRangeOption = (): TimeRangeOption => ({ to: "now", }); -export type VariableKind = QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind; +export type VariableKind = QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind | SwitchVariableKind; export const defaultVariableKind = (): VariableKind => (defaultQueryVariableKind()); @@ -1444,6 +1444,36 @@ export const defaultMetricFindValue = (): MetricFindValue => ({ text: "", }); +export interface SwitchVariableKind { + kind: "SwitchVariable"; + spec: SwitchVariableSpec; +} + +export const defaultSwitchVariableKind = (): SwitchVariableKind => ({ + kind: "SwitchVariable", + spec: defaultSwitchVariableSpec(), +}); + +export interface SwitchVariableSpec { + name: string; + current: string; + enabledValue: string; + disabledValue: string; + label?: string; + hide: VariableHide; + skipUrlSync: boolean; + description?: string; +} + +export const defaultSwitchVariableSpec = (): SwitchVariableSpec => ({ + name: "", + current: "false", + enabledValue: "true", + disabledValue: "false", + hide: "dontHide", + skipUrlSync: false, +}); + export interface Spec { annotations: AnnotationQueryKind[]; // Configuration of dashboard cursor sync behavior. diff --git a/packages/grafana-schema/src/schema/dashboard/v2beta1/types.status.gen.ts b/packages/grafana-schema/src/schema/dashboard/v2beta1/types.status.gen.ts index 91bf7909fd3..978b71f39b3 100644 --- a/packages/grafana-schema/src/schema/dashboard/v2beta1/types.status.gen.ts +++ b/packages/grafana-schema/src/schema/dashboard/v2beta1/types.status.gen.ts @@ -12,6 +12,8 @@ export interface ConversionStatus { // The version which was stored when the dashboard was created / updated. // Fetching this version should always succeed. storedVersion?: string; + // The original value map[string]any + source?: any; } export const defaultConversionStatus = (): ConversionStatus => ({ diff --git a/packages/grafana-schema/src/veneer/librarypanel.types.ts b/packages/grafana-schema/src/veneer/librarypanel.types.ts index 007bb085742..0065ebe25e2 100644 --- a/packages/grafana-schema/src/veneer/librarypanel.types.ts +++ b/packages/grafana-schema/src/veneer/librarypanel.types.ts @@ -1,7 +1,55 @@ -import * as raw from '../raw/librarypanel/x/librarypanel_types.gen'; - import { Panel } from './dashboard.types'; -export interface LibraryPanel extends raw.LibraryPanel { +export interface LibraryElementDTOMetaUser { + avatarUrl: string; + id: number; + name: string; +} + +export interface LibraryElementDTOMeta { + connectedDashboards: number; + created: string; + createdBy: LibraryElementDTOMetaUser; + folderName: string; + folderUid: string; + updated: string; + updatedBy: LibraryElementDTOMetaUser; +} + +export interface LibraryPanel { + /** + * Panel description + */ + description?: string; + /** + * Folder UID + */ + folderUid?: string; + /** + * Object storage metadata + */ + meta?: LibraryElementDTOMeta; + + // The panel model: Omit; + /** + * Panel name (also saved in the model) + */ + name: string; + /** + * Dashboard version when this was saved (zero if unknown) + */ + schemaVersion?: number; + /** + * The panel type (from inside the model) + */ + type: string; + /** + * Library element UID + */ + uid: string; + /** + * panel version, incremented each time the dashboard is updated. + */ + version: number; } diff --git a/packages/grafana-test-utils/src/handlers/all-handlers.ts b/packages/grafana-test-utils/src/handlers/all-handlers.ts index b3b70fbd4cc..3b1c7eb7017 100644 --- a/packages/grafana-test-utils/src/handlers/all-handlers.ts +++ b/packages/grafana-test-utils/src/handlers/all-handlers.ts @@ -1,6 +1,7 @@ import { HttpHandler } from 'msw'; import folderHandlers from './api/folders/handlers'; +import pluginsHandlers from './api/plugins/handlers'; import searchHandlers from './api/search/handlers'; import teamsHandlers from './api/teams/handlers'; import userHandlers from './api/user/handlers'; @@ -13,6 +14,7 @@ const allHandlers: HttpHandler[] = [ ...teamsHandlers, ...folderHandlers, ...searchHandlers, + ...pluginsHandlers, ...userHandlers, // App platform handlers diff --git a/packages/grafana-test-utils/src/handlers/api/plugins/handlers.ts b/packages/grafana-test-utils/src/handlers/api/plugins/handlers.ts new file mode 100644 index 00000000000..0acf25b8bf1 --- /dev/null +++ b/packages/grafana-test-utils/src/handlers/api/plugins/handlers.ts @@ -0,0 +1,6 @@ +import { HttpResponse, http } from 'msw'; + +// TODO: Add more realistic mock plugins data +const getPluginsHandler = () => http.get('/api/plugins', () => HttpResponse.json([])); + +export default [getPluginsHandler()]; diff --git a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx index a1bdc9a945f..63044d76d2f 100644 --- a/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx +++ b/packages/grafana-ui/src/components/ErrorBoundary/ErrorBoundary.tsx @@ -44,8 +44,8 @@ export class ErrorBoundary extends PureComponent { this.props.errorLogger(error); } else { faro?.api?.pushError(error, { - type: 'boundary', context: { + type: 'boundary', source: this.props.boundaryName ?? 'unknown', }, }); diff --git a/packages/grafana-ui/src/components/Modal/getModalStyles.ts b/packages/grafana-ui/src/components/Modal/getModalStyles.ts index d6ef33245b6..8defbe310a6 100644 --- a/packages/grafana-ui/src/components/Modal/getModalStyles.ts +++ b/packages/grafana-ui/src/components/Modal/getModalStyles.ts @@ -75,11 +75,17 @@ export const getModalStyles = (theme: GrafanaTheme2) => { }), modalContent: css({ overflow: 'auto', - padding: theme.spacing(3), + padding: theme.spacing(3, 3, 0, 3), + marginBottom: theme.spacing(3), + scrollbarWidth: 'thin', width: '100%', }), modalButtonRow: css({ - paddingTop: theme.spacing(3), + background: theme.colors.background.primary, + position: 'sticky', + bottom: 0, + paddingTop: theme.spacing(2), + zIndex: 1, }), }; }; diff --git a/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx b/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx index 6128326b5e8..36fccb70159 100644 --- a/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx +++ b/packages/grafana-ui/src/components/ThemeDemos/ThemeDemo.tsx @@ -18,17 +18,20 @@ import { Icon } from '../Icon/Icon'; import { Input } from '../Input/Input'; import { BackgroundColor, BorderColor, Box, BoxShadow } from '../Layout/Box/Box'; import { Stack } from '../Layout/Stack/Stack'; +import { ScrollContainer } from '../ScrollContainer/ScrollContainer'; import { Switch } from '../Switch/Switch'; import { Text, TextProps } from '../Text/Text'; interface DemoBoxProps { bg?: BackgroundColor; border?: BorderColor; + scrollable?: boolean; shadow?: BoxShadow; textColor?: TextProps['color']; } -const DemoBox = ({ bg, border, children, shadow }: React.PropsWithChildren) => { +const DemoBox = ({ bg, border, children, shadow, scrollable }: React.PropsWithChildren) => { + const MaybeScroll = scrollable ? ScrollContainer : React.Fragment; return ( - {children} + {children} ); }; @@ -121,7 +124,7 @@ export const ThemeDemo = () => { - + @@ -134,8 +137,8 @@ export const ThemeDemo = () => { - - + +
@@ -154,8 +157,8 @@ export const ThemeDemo = () => { - -
name
+ +
@@ -229,7 +232,7 @@ export const ThemeDemo = () => { - + {allButtonVariants.map((variant) => ( + {options.map((opt) => ( + + ))} + + + ); +} diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx index d021eac7889..fbefcfbbc64 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManager.tsx @@ -9,19 +9,25 @@ import { VizPanel, } from '@grafana/scenes'; import { Spec as DashboardV2Spec } from '@grafana/schema/dist/esm/schema/dashboard/v2'; +import appEvents from 'app/core/app_events'; +import { ShowConfirmModalEvent, ShowModalReactEvent } from 'app/types/events'; import { dashboardEditActions, ObjectsReorderedOnCanvasEvent } from '../../edit-pane/shared'; import { serializeRowsLayout } from '../../serialization/layoutSerializers/RowsLayoutSerializer'; import { getDashboardSceneFor } from '../../utils/utils'; +import { AutoGridLayoutManager } from '../layout-auto-grid/AutoGridLayoutManager'; import { DashboardGridItem } from '../layout-default/DashboardGridItem'; import { DefaultGridLayoutManager } from '../layout-default/DefaultGridLayoutManager'; import { RowRepeaterBehavior } from '../layout-default/RowRepeaterBehavior'; import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager'; +import { findAllGridTypes } from '../layouts-shared/findAllGridTypes'; import { getRowFromClipboard } from '../layouts-shared/paste'; import { generateUniqueTitle, ungroupLayout } from '../layouts-shared/utils'; import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; +import { isLayoutParent } from '../types/LayoutParent'; import { LayoutRegistryItem } from '../types/LayoutRegistryItem'; +import { ConvertMixedGridsModal } from './ConvertMixedGridsModal'; import { RowItem } from './RowItem'; import { RowLayoutManagerRenderer } from './RowsLayoutManagerRenderer'; @@ -29,6 +35,22 @@ interface RowsLayoutManagerState extends SceneObjectState { rows: RowItem[]; } +enum GridLayoutType { + AutoGridLayout = 'AutoGridLayout', + GridLayout = 'GridLayout', +} + +function mapIdToGridLayoutType(id?: string): GridLayoutType | undefined { + switch (id) { + case GridLayoutType.AutoGridLayout: + return GridLayoutType.AutoGridLayout; + case GridLayoutType.GridLayout: + return GridLayoutType.GridLayout; + default: + return undefined; + } +} + export class RowsLayoutManager extends SceneObjectBase implements DashboardLayoutManager { public static Component = RowLayoutManagerRenderer; public readonly isDashboardLayoutManager = true; @@ -128,25 +150,155 @@ export class RowsLayoutManager extends SceneObjectBase i return outlineChildren; } - public removeRow(row: RowItem) { + public convertAllRowsLayouts(gridLayoutType: GridLayoutType) { + for (const row of this.state.rows) { + switch (gridLayoutType) { + case GridLayoutType.AutoGridLayout: + if (!(row.getLayout() instanceof AutoGridLayoutManager)) { + row.switchLayout(AutoGridLayoutManager.createFromLayout(row.getLayout())); + } + break; + case GridLayoutType.GridLayout: + if (!(row.getLayout() instanceof DefaultGridLayoutManager)) { + row.switchLayout(DefaultGridLayoutManager.createFromLayout(row.getLayout())); + } + break; + } + } + } + + public ungroupRows() { + const hasNonGridLayout = this.state.rows.some((row) => !row.getLayout().descriptor.isGridLayout); + const gridTypes = new Set(findAllGridTypes(this)); + + if (hasNonGridLayout) { + appEvents.publish( + new ShowConfirmModalEvent({ + title: t('dashboard.rows-layout.ungroup-nested-title', 'Ungroup nested groups?'), + text: t('dashboard.rows-layout.ungroup-nested-text', 'This will ungroup all nested groups.'), + yesText: t('dashboard.rows-layout.continue', 'Continue'), + noText: t('dashboard.rows-layout.cancel', 'Cancel'), + onConfirm: () => { + if (gridTypes.size > 1) { + requestAnimationFrame(() => { + this._confirmConvertMixedGrids(gridTypes); + }); + } else { + this.wrapUngroupRowsInEdit(mapIdToGridLayoutType(gridTypes.values().next().value)!); + } + }, + }) + ); + return; + } + + if (gridTypes.size > 1) { + this._confirmConvertMixedGrids(gridTypes); + return; + } else { + this.wrapUngroupRowsInEdit(mapIdToGridLayoutType(gridTypes.values().next().value)!); + } + } + + private _confirmConvertMixedGrids(availableIds: Set) { + appEvents.publish( + new ShowModalReactEvent({ + component: ConvertMixedGridsModal, + props: { + availableIds, + onSelect: (id: string) => { + const selected = mapIdToGridLayoutType(id); + if (selected) { + this.wrapUngroupRowsInEdit(selected); + } + }, + }, + }) + ); + } + + private wrapUngroupRowsInEdit(gridLayoutType: GridLayoutType) { + const parent = this.parent; + if (!parent || !isLayoutParent(parent)) { + throw new Error('Ungroup rows failed: parent is not a layout container'); + } + + const previousLayout = this.clone({}); + const scene = getDashboardSceneFor(this); + + dashboardEditActions.edit({ + description: t('dashboard.rows-layout.edit.ungroup-rows', 'Ungroup rows'), + source: scene, + perform: () => { + this._ungroupRows(gridLayoutType); + }, + undo: () => { + parent.switchLayout(previousLayout); + }, + }); + } + + private _ungroupRows(gridLayoutType: GridLayoutType) { + const hasNonGridLayout = this.state.rows.some((row) => !row.getLayout().descriptor.isGridLayout); + + if (hasNonGridLayout) { + for (const row of this.state.rows) { + const layout = row.getLayout(); + if (!layout.descriptor.isGridLayout) { + if (layout instanceof RowsLayoutManager) { + layout._ungroupRows(gridLayoutType); + } else { + throw new Error(`Ungrouping not supported for layout type: ${layout.descriptor.name}`); + } + } + } + } + + this.convertAllRowsLayouts(gridLayoutType); + + const firstRow = this.state.rows[0]; + const firstRowLayout = firstRow.getLayout(); + const otherRows = this.state.rows.slice(1); + + for (const row of otherRows) { + const layout = row.getLayout(); + if (firstRowLayout.merge) { + firstRowLayout.merge(layout); + } else { + throw new Error(`Layout type ${firstRowLayout.descriptor.name} does not support merging`); + } + } + + this.setState({ rows: [firstRow] }); + this.removeRow(firstRow, true); + } + + public removeRow(row: RowItem, skipUndo?: boolean) { // When removing last row replace ourselves with the inner row layout if (this.shouldUngroup()) { - ungroupLayout(this, row.state.layout); + ungroupLayout(this, row.state.layout, skipUndo ?? false); return; } const indexOfRowToRemove = this.state.rows.findIndex((r) => r === row); - dashboardEditActions.removeElement({ - removedObject: row, - source: this, - perform: () => this.setState({ rows: this.state.rows.filter((r) => r !== row) }), - undo: () => { - const rows = [...this.state.rows]; - rows.splice(indexOfRowToRemove, 0, row); - this.setState({ rows }); - }, - }); + const perform = () => this.setState({ rows: this.state.rows.filter((r) => r !== row) }); + const undo = () => { + const rows = [...this.state.rows]; + rows.splice(indexOfRowToRemove, 0, row); + this.setState({ rows }); + }; + + if (skipUndo) { + perform(); + } else { + dashboardEditActions.removeElement({ + removedObject: row, + source: this, + perform, + undo, + }); + } } public moveRow(_rowKey: string, fromIndex: number, toIndex: number) { @@ -212,7 +364,7 @@ export class RowsLayoutManager extends SceneObjectBase i if (child instanceof SceneGridRow) { // Skip repeated row clones - if (child.state.repeatSourceKey) { + if ('repeatSourceKey' in child.state && child.state.repeatSourceKey) { return; } diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManagerRenderer.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManagerRenderer.tsx index b18634bdd00..d7de6c42413 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManagerRenderer.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowsLayoutManagerRenderer.tsx @@ -53,6 +53,9 @@ export function RowLayoutManagerRenderer({ model }: SceneComponentProps + diff --git a/public/app/features/dashboard-scene/scene/layouts-shared/DashboardLayoutSelector.tsx b/public/app/features/dashboard-scene/scene/layouts-shared/DashboardLayoutSelector.tsx index 125bd380256..f4714e7d420 100644 --- a/public/app/features/dashboard-scene/scene/layouts-shared/DashboardLayoutSelector.tsx +++ b/public/app/features/dashboard-scene/scene/layouts-shared/DashboardLayoutSelector.tsx @@ -1,10 +1,12 @@ import { useCallback, useMemo } from 'react'; import { t } from '@grafana/i18n'; +import { config } from '@grafana/runtime'; import { RadioButtonGroup, Box } from '@grafana/ui'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor'; +import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager'; import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; import { isLayoutParent } from '../types/LayoutParent'; import { LayoutRegistryItem } from '../types/LayoutRegistryItem'; @@ -19,6 +21,21 @@ export function DashboardLayoutSelector({ layoutManager }: Props) { const isGridLayout = layoutManager.descriptor.isGridLayout; const options = layoutRegistry.list().filter((layout) => layout.isGridLayout === isGridLayout); + const disableTabs = useMemo(() => { + if (config.featureToggles.unlimitedLayoutsNesting) { + return false; + } + let parent = layoutManager.parent; + while (parent) { + if (parent instanceof TabsLayoutManager) { + return true; + } + parent = parent.parent; + } + + return false; + }, [layoutManager]); + const onChangeLayout = useCallback( (newLayout: LayoutRegistryItem) => { const layoutParent = layoutManager.parent; @@ -30,17 +47,33 @@ export function DashboardLayoutSelector({ layoutManager }: Props) { [layoutManager] ); - const radioOptions = options.map((opt) => ({ - value: opt, - label: opt.name, - icon: opt.icon, - description: opt.description, - ariaLabel: `layout-selection-option-${opt.name}`, - })); + const disabledOptions: LayoutRegistryItem[] = []; + + const radioOptions = options.map((opt) => { + let description = opt.description; + if (disableTabs && opt.id === TabsLayoutManager.descriptor.id) { + description = t('dashboard.canvas-actions.disabled-nested-tabs', 'Tabs cannot be nested inside other tabs'); + disabledOptions.push(opt); + } + + return { + value: opt, + label: opt.name, + icon: opt.icon, + description, + ariaLabel: `layout-selection-option-${opt.name}`, + }; + }); return ( - + ); } diff --git a/public/app/features/dashboard-scene/scene/layouts-shared/findAllGridTypes.ts b/public/app/features/dashboard-scene/scene/layouts-shared/findAllGridTypes.ts new file mode 100644 index 00000000000..03dec1482fb --- /dev/null +++ b/public/app/features/dashboard-scene/scene/layouts-shared/findAllGridTypes.ts @@ -0,0 +1,17 @@ +import { RowsLayoutManager } from '../layout-rows/RowsLayoutManager'; +import { TabsLayoutManager } from '../layout-tabs/TabsLayoutManager'; +import { DashboardLayoutManager } from '../types/DashboardLayoutManager'; + +export function findAllGridTypes(layout: DashboardLayoutManager): string[] { + if (layout.descriptor.isGridLayout) { + return [layout.descriptor.id]; + } + + if (layout instanceof TabsLayoutManager) { + return layout.state.tabs.flatMap((tab) => findAllGridTypes(tab.getLayout())); + } else if (layout instanceof RowsLayoutManager) { + return layout.state.rows.flatMap((row) => findAllGridTypes(row.getLayout())); + } + + return []; +} diff --git a/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts b/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts index eac9ff86333..fa1cc4b6ae5 100644 --- a/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts +++ b/public/app/features/dashboard-scene/scene/layouts-shared/utils.ts @@ -69,11 +69,11 @@ export function generateUniqueTitle(title: string | undefined, existingTitles: S return baseTitle; } -export function ungroupLayout(layout: DashboardLayoutManager, innerLayout: DashboardLayoutManager) { +export function ungroupLayout(layout: DashboardLayoutManager, innerLayout: DashboardLayoutManager, skipUndo?: boolean) { const layoutParent = layout.parent!; if (isLayoutParent(layoutParent)) { innerLayout.clearParent(); - layoutParent.switchLayout(innerLayout); + layoutParent.switchLayout(innerLayout, skipUndo); } } diff --git a/public/app/features/dashboard-scene/scene/types/DashboardLayoutManager.ts b/public/app/features/dashboard-scene/scene/types/DashboardLayoutManager.ts index 73a9d7329e0..0db0463bc22 100644 --- a/public/app/features/dashboard-scene/scene/types/DashboardLayoutManager.ts +++ b/public/app/features/dashboard-scene/scene/types/DashboardLayoutManager.ts @@ -86,6 +86,11 @@ export interface DashboardLayoutManager extends SceneObject { * Get children for outline */ getOutlineChildren(): SceneObject[]; + + /** + * Merge the layout with another layout + */ + merge?(other: DashboardLayoutManager): void; } export interface LayoutManagerSerializer { diff --git a/public/app/features/dashboard-scene/scene/types/LayoutParent.ts b/public/app/features/dashboard-scene/scene/types/LayoutParent.ts index 71601f9bbed..e1dc81a6c43 100644 --- a/public/app/features/dashboard-scene/scene/types/LayoutParent.ts +++ b/public/app/features/dashboard-scene/scene/types/LayoutParent.ts @@ -13,9 +13,10 @@ export interface LayoutParent extends SceneObject { /** * Switches the inner layout manager - * @param newLayout + * @param newLayout The new layout manager to switch to + * @param skipUndo If true, skips creating an undo entry for this operation */ - switchLayout(newLayout: DashboardLayoutManager): void; + switchLayout(newLayout: DashboardLayoutManager, skipUndo?: boolean): void; } export function isLayoutParent(obj: SceneObject): obj is LayoutParent { diff --git a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts index e91b28cc646..8772646b496 100644 --- a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts +++ b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.test.ts @@ -21,6 +21,7 @@ import { IntervalVariable, QueryVariable, SceneVariableSet, + SwitchVariable, TextBoxVariable, } from '@grafana/scenes'; import { DataSourceRef, VariableHide, VariableRefresh } from '@grafana/schema'; @@ -877,6 +878,168 @@ describe('sceneVariablesSetToVariables', () => { }); }); + it('should handle SwitchVariable with "true" value', () => { + const variable = new SwitchVariable({ + name: 'test', + label: 'test-label', + description: 'test-desc', + hide: VariableHide.inControlsMenu, + value: 'true', + skipUrlSync: true, + }); + const set = new SceneVariableSet({ + variables: [variable], + }); + + const result = sceneVariablesSetToVariables(set); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchInlineSnapshot(` +{ + "current": { + "text": "true", + "value": "true", + }, + "description": "test-desc", + "hide": 3, + "label": "test-label", + "name": "test", + "options": [ + { + "text": "true", + "value": "true", + }, + { + "text": "false", + "value": "false", + }, + ], + "skipUrlSync": true, + "type": "switch", +} +`); + }); + + it('should handle SwitchVariable with "false" value', () => { + const variable = new SwitchVariable({ + name: 'test', + label: 'test-label', + description: 'test-desc', + hide: VariableHide.inControlsMenu, + value: 'false', + skipUrlSync: false, + }); + const set = new SceneVariableSet({ + variables: [variable], + }); + + const result = sceneVariablesSetToVariables(set); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchInlineSnapshot(` +{ + "current": { + "text": "false", + "value": "false", + }, + "description": "test-desc", + "hide": 3, + "label": "test-label", + "name": "test", + "options": [ + { + "text": "true", + "value": "true", + }, + { + "text": "false", + "value": "false", + }, + ], + "type": "switch", +} +`); + }); + + it('should handle SwitchVariable with custom values', () => { + const variable = new SwitchVariable({ + name: 'test', + label: 'test-label', + description: 'test-desc', + hide: VariableHide.inControlsMenu, + value: 'on', + enabledValue: 'on', + disabledValue: 'off', + skipUrlSync: false, + }); + const set = new SceneVariableSet({ + variables: [variable], + }); + + const result = sceneVariablesSetToVariables(set); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchInlineSnapshot(` +{ + "current": { + "text": "on", + "value": "on", + }, + "description": "test-desc", + "hide": 3, + "label": "test-label", + "name": "test", + "options": [ + { + "text": "on", + "value": "on", + }, + { + "text": "off", + "value": "off", + }, + ], + "type": "switch", +} +`); + }); + + it('should handle SwitchVariable with minimal configuration', () => { + const variable = new SwitchVariable({ + name: 'minimal', + value: 'true', + }); + const set = new SceneVariableSet({ + variables: [variable], + }); + + const result = sceneVariablesSetToVariables(set); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchInlineSnapshot(` +{ + "current": { + "text": "true", + "value": "true", + }, + "description": undefined, + "label": undefined, + "name": "minimal", + "options": [ + { + "text": "true", + "value": "true", + }, + { + "text": "false", + "value": "false", + }, + ], + "type": "switch", +} +`); + }); + describe('sceneVariablesSetToSchemaV2Variables', () => { it('should handle QueryVariable', () => { const variable = new QueryVariable({ @@ -1455,5 +1618,100 @@ describe('sceneVariablesSetToVariables', () => { expect(() => sceneVariablesSetToSchemaV2Variables(set)).toThrow('Unsupported variable type'); }); }); + + it('should handle SwitchVariable with true value', () => { + const variable = new SwitchVariable({ + name: 'test', + label: 'test-label', + description: 'test-desc', + hide: VariableHide.inControlsMenu, + value: 'true', + skipUrlSync: true, + }); + const set = new SceneVariableSet({ + variables: [variable], + }); + + const result = sceneVariablesSetToSchemaV2Variables(set); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchInlineSnapshot(` +{ + "kind": "SwitchVariable", + "spec": { + "current": "true", + "description": "test-desc", + "disabledValue": "false", + "enabledValue": "true", + "hide": "inControlsMenu", + "label": "test-label", + "name": "test", + "skipUrlSync": true, + }, +} +`); + }); + + it('should handle SwitchVariable with false value', () => { + const variable = new SwitchVariable({ + name: 'test', + label: 'test-label', + description: 'test-desc', + hide: VariableHide.inControlsMenu, + value: 'false', + skipUrlSync: false, + }); + const set = new SceneVariableSet({ + variables: [variable], + }); + + const result = sceneVariablesSetToSchemaV2Variables(set); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchInlineSnapshot(` +{ + "kind": "SwitchVariable", + "spec": { + "current": "false", + "description": "test-desc", + "disabledValue": "false", + "enabledValue": "true", + "hide": "inControlsMenu", + "label": "test-label", + "name": "test", + "skipUrlSync": false, + }, +} +`); + }); + + it('should handle SwitchVariable with minimal configuration', () => { + const variable = new SwitchVariable({ + name: 'minimal', + value: 'true', + }); + const set = new SceneVariableSet({ + variables: [variable], + }); + + const result = sceneVariablesSetToSchemaV2Variables(set); + + expect(result).toHaveLength(1); + expect(result[0]).toMatchInlineSnapshot(` +{ + "kind": "SwitchVariable", + "spec": { + "current": "true", + "description": undefined, + "disabledValue": "false", + "enabledValue": "true", + "hide": "dontHide", + "label": undefined, + "name": "minimal", + "skipUrlSync": false, + }, +} +`); + }); }); }); diff --git a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts index 87cde15a7ea..01fe0927619 100644 --- a/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts +++ b/public/app/features/dashboard-scene/serialization/sceneVariablesSetToVariables.ts @@ -26,6 +26,7 @@ import { VariableOption, defaultDataQueryKind, AdHocFilterWithLabels, + SwitchVariableKind, } from '@grafana/schema/dist/esm/schema/dashboard/v2'; import { getDefaultDatasource } from 'app/features/dashboard/api/ResponseTransformers'; @@ -210,6 +211,24 @@ export function sceneVariablesSetToVariables(set: SceneVariables, keepQueryOptio filters: [...validateFiltersOrigin(variable.state.originFilters), ...variable.state.filters], defaultKeys: variable.state.defaultKeys, }); + } else if (sceneUtils.isSwitchVariable(variable)) { + variables.push({ + ...commonProperties, + current: { + value: variable.state.value, + text: variable.state.value, + }, + options: [ + { + value: variable.state.enabledValue, + text: variable.state.enabledValue, + }, + { + value: variable.state.disabledValue, + text: variable.state.disabledValue, + }, + ], + }); } else if (variable.state.type === 'system') { // Not persisted } else { @@ -264,6 +283,7 @@ export function sceneVariablesSetToSchemaV2Variables( | ConstantVariableKind | GroupByVariableKind | AdhocVariableKind + | SwitchVariableKind > { let variables: Array< | QueryVariableKind @@ -274,6 +294,7 @@ export function sceneVariablesSetToSchemaV2Variables( | ConstantVariableKind | GroupByVariableKind | AdhocVariableKind + | SwitchVariableKind > = []; for (const variable of set.state.variables) { @@ -294,6 +315,8 @@ export function sceneVariablesSetToSchemaV2Variables( }; let options: VariableOption[] = []; + + // Query variable if (sceneUtils.isQueryVariable(variable)) { // Not sure if we actually have to still support this option given // that it's not exposed in the UI @@ -355,6 +378,8 @@ export function sceneVariablesSetToSchemaV2Variables( }, }; variables.push(queryVariable); + + // Custom variable } else if (sceneUtils.isCustomVariable(variable)) { options = variableValueOptionsToVariableOptions(variable.state); const customVariable: CustomVariableKind = { @@ -371,6 +396,8 @@ export function sceneVariablesSetToSchemaV2Variables( }, }; variables.push(customVariable); + + // Datasource variable } else if (sceneUtils.isDataSourceVariable(variable)) { const datasourceVariable: DatasourceVariableKind = { kind: 'DatasourceVariable', @@ -392,6 +419,8 @@ export function sceneVariablesSetToSchemaV2Variables( } variables.push(datasourceVariable); + + // Constant variable } else if (sceneUtils.isConstantVariable(variable)) { const constantVariable: ConstantVariableKind = { kind: 'ConstantVariable', @@ -407,6 +436,8 @@ export function sceneVariablesSetToSchemaV2Variables( }, }; variables.push(constantVariable); + + // Interval variable } else if (sceneUtils.isIntervalVariable(variable)) { const intervals = getIntervalsQueryFromNewIntervalModel(variable.state.intervals); const intervalVariable: IntervalVariableKind = { @@ -431,6 +462,8 @@ export function sceneVariablesSetToSchemaV2Variables( }, }; variables.push(intervalVariable); + + // Textbox variable } else if (sceneUtils.isTextBoxVariable(variable)) { const current = { text: variable.state.value, @@ -447,6 +480,8 @@ export function sceneVariablesSetToSchemaV2Variables( }; variables.push(textBoxVariable); + + // Groupby variable } else if (sceneUtils.isGroupByVariable(variable) && config.featureToggles.groupByVariable) { options = variableValueOptionsToVariableOptions(variable.state); @@ -483,6 +518,8 @@ export function sceneVariablesSetToSchemaV2Variables( }, }; variables.push(groupVariable); + + // Adhoc variable } else if (sceneUtils.isAdHocVariable(variable)) { const ds = getDataSourceForQuery( variable.state.datasource, @@ -508,6 +545,19 @@ export function sceneVariablesSetToSchemaV2Variables( }, }; variables.push(adhocVariable); + + // Switch variable + } else if (sceneUtils.isSwitchVariable(variable)) { + const switchVariable: SwitchVariableKind = { + kind: 'SwitchVariable', + spec: { + ...commonProperties, + current: variable.state.value, + enabledValue: variable.state.enabledValue, + disabledValue: variable.state.disabledValue, + }, + }; + variables.push(switchVariable); } else if (variable.state.type === 'system') { // Do nothing } else { diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts index d352c75a5ec..ad94f404e68 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelSchemaV2ToScene.ts @@ -16,6 +16,7 @@ import { SceneVariable, SceneVariableSet, ScopesVariable, + SwitchVariable, TextBoxVariable, } from '@grafana/scenes'; import { @@ -34,11 +35,13 @@ import { defaultIntervalVariableKind, defaultQueryVariableKind, defaultTextVariableKind, + defaultSwitchVariableKind, GroupByVariableKind, IntervalVariableKind, LibraryPanelKind, PanelKind, QueryVariableKind, + SwitchVariableKind, TextVariableKind, } from '@grafana/schema/dist/esm/schema/dashboard/v2'; import { DEFAULT_ANNOTATION_COLOR } from '@grafana/ui'; @@ -92,7 +95,8 @@ export type TypedVariableModelV2 = | IntervalVariableKind | CustomVariableKind | GroupByVariableKind - | AdhocVariableKind; + | AdhocVariableKind + | SwitchVariableKind; export function transformSaveModelSchemaV2ToScene(dto: DashboardWithAccessInfo): DashboardScene { const { spec: dashboard, metadata, apiVersion } = dto; @@ -415,6 +419,15 @@ function createSceneVariableFromVariableModel(variable: TypedVariableModelV2): S // @ts-expect-error defaultOptions: variable.options, }); + } else if (variable.kind === defaultSwitchVariableKind().kind) { + return new SwitchVariable({ + ...commonProperties, + value: variable.spec.current ?? 'false', + enabledValue: variable.spec.enabledValue ?? 'true', + disabledValue: variable.spec.disabledValue ?? 'false', + skipUrlSync: variable.spec.skipUrlSync, + hide: transformVariableHideToEnumV1(variable.spec.hide), + }); } else { throw new Error(`Scenes: Unsupported variable type ${variable.kind}`); } @@ -520,6 +533,11 @@ export function createSnapshotVariable(variable: TypedVariableModelV2): SceneVar value: '', text: '', }; + } else if (variable.kind === 'SwitchVariable') { + current = { + value: variable.spec.current ?? 'false', + text: variable.spec.current ?? 'false', + }; } else { current = { value: variable.spec.current?.value ?? '', diff --git a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts index dd92ca69b00..a1d379d9865 100644 --- a/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts +++ b/public/app/features/dashboard-scene/serialization/transformSceneToSaveModelSchemaV2.ts @@ -44,6 +44,7 @@ import { FieldColor, defaultFieldConfig, defaultDataQueryKind, + SwitchVariableKind, } from '../../../../../packages/grafana-schema/src/schema/dashboard/v2'; import { DashboardDataLayerSet } from '../scene/DashboardDataLayerSet'; import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene'; @@ -411,6 +412,7 @@ function getVariables(oldDash: DashboardSceneState, dsReferencesMapping?: DSRefe | ConstantVariableKind | GroupByVariableKind | AdhocVariableKind + | SwitchVariableKind > = []; if (variablesSet instanceof SceneVariableSet) { diff --git a/public/app/features/dashboard-scene/settings/variables/VariablesUnknownTable.tsx b/public/app/features/dashboard-scene/settings/variables/VariablesUnknownTable.tsx index da216fd78ba..c392066b08f 100644 --- a/public/app/features/dashboard-scene/settings/variables/VariablesUnknownTable.tsx +++ b/public/app/features/dashboard-scene/settings/variables/VariablesUnknownTable.tsx @@ -6,7 +6,7 @@ import { GrafanaTheme2 } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { reportInteraction } from '@grafana/runtime'; import { SceneVariable, SceneVariableState } from '@grafana/scenes'; -import { Dashboard } from '@grafana/schema/dist/esm/index.gen'; +import { Dashboard } from '@grafana/schema'; import { CollapsableSection, Icon, Spinner, Stack, Tooltip, useStyles2 } from '@grafana/ui'; import { VariableUsagesButton } from '../../variables/VariableUsagesButton'; diff --git a/public/app/features/dashboard-scene/settings/variables/components/CustomVariableForm.tsx b/public/app/features/dashboard-scene/settings/variables/components/CustomVariableForm.tsx index f11037fb80c..b3c78330156 100644 --- a/public/app/features/dashboard-scene/settings/variables/components/CustomVariableForm.tsx +++ b/public/app/features/dashboard-scene/settings/variables/components/CustomVariableForm.tsx @@ -1,16 +1,11 @@ import { FormEvent } from 'react'; -import { lastValueFrom } from 'rxjs'; import { selectors } from '@grafana/e2e-selectors'; import { Trans, t } from '@grafana/i18n'; -import { CustomVariable, SceneVariable } from '@grafana/scenes'; -import { TextArea } from '@grafana/ui'; -import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor'; - -import { VariableLegend } from '../components/VariableLegend'; -import { VariableTextAreaField } from '../components/VariableTextAreaField'; import { SelectionOptionsForm } from './SelectionOptionsForm'; +import { VariableLegend } from './VariableLegend'; +import { VariableTextAreaField } from './VariableTextAreaField'; interface CustomVariableFormProps { query: string; @@ -71,41 +66,3 @@ export function CustomVariableForm({ ); } - -export function getCustomVariableOptions(variable: SceneVariable): OptionsPaneItemDescriptor[] { - if (!(variable instanceof CustomVariable)) { - return []; - } - - return [ - new OptionsPaneItemDescriptor({ - title: t('dashboard.edit-pane.variable.custom-options.values', 'Values separated by comma'), - id: 'custom-variable-values', - render: (descriptor) => , - }), - ]; -} - -function ValuesTextField({ variable, id }: { variable: CustomVariable; id?: string }) { - const { query } = variable.useState(); - - const onBlur = async (event: FormEvent) => { - variable.setState({ query: event.currentTarget.value }); - await lastValueFrom(variable.validateAndUpdate!()); - }; - - return ( -
name