Merge remote-tracking branch 'origin/main' into secret-service/feature-branch

pull/105953/head^2
Dana Axinte 4 weeks ago
commit f5acaacfa9
  1. 3
      .betterer.results
  2. 8
      .drone.yml
  3. 7
      .github/CODEOWNERS
  4. 8
      .github/workflows/detect-breaking-changes-levitate.yml
  5. 4
      .github/workflows/e2e-dashboard-new-layouts.yml
  6. 2
      .github/workflows/lint-build-docs.yml
  7. 61
      .github/workflows/pr-e2e-tests.yml
  8. 67
      .github/workflows/publish-artifact.yml
  9. 105
      .github/workflows/release-build.yml
  10. 2
      .github/workflows/run-dashboard-search-e2e.yml
  11. 6
      .github/workflows/run-schema-v2-e2e.yml
  12. 2
      .github/workflows/storybook-verification.yml
  13. 1
      .gitignore
  14. 140
      .pa11yci.conf.js
  15. 934
      .yarn/releases/yarn-4.6.0.cjs
  16. 942
      .yarn/releases/yarn-4.9.2.cjs
  17. 2
      .yarnrc.yml
  18. 12
      apps/advisor/pkg/app/checkscheduler/checkscheduler.go
  19. 11
      apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer.go
  20. 18
      apps/advisor/pkg/app/checktyperegisterer/checktyperegisterer_test.go
  21. 6
      apps/advisor/pkg/app/utils.go
  22. 5
      apps/advisor/pkg/app/utils_test.go
  23. 3
      apps/alerting/notifications/go.mod
  24. 4
      apps/alerting/notifications/go.sum
  25. 14
      apps/dashboard/go.mod
  26. 28
      apps/dashboard/go.sum
  27. 99
      devenv/dev-dashboards/panel-timeline/timeline-thresholds-mappings.json
  28. 21
      docs/sources/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/index.md
  29. 302
      docs/sources/dashboards/create-reports/_index.md
  30. 15
      docs/sources/dashboards/create-reports/report-settings/index.md
  31. 4
      docs/sources/setup-grafana/configure-grafana/_index.md
  32. 1
      docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
  33. 2
      docs/sources/setup-grafana/configure-security/configure-security-hardening/index.md
  34. 2
      e2e/old-arch/smoke-tests-suite/panels_smokescreen.spec.ts
  35. 126
      e2e/pa11yci.conf.js
  36. 19
      go.mod
  37. 38
      go.sum
  38. 16
      go.work.sum
  39. 11
      package.json
  40. 5
      packages/grafana-data/src/types/featureToggles.gen.ts
  41. 2
      packages/grafana-plugin-configs/package.json
  42. 2
      packages/grafana-prometheus/package.json
  43. 35
      packages/grafana-prometheus/src/language_provider.test.ts
  44. 11
      packages/grafana-prometheus/src/language_provider.ts
  45. 8
      packages/grafana-prometheus/src/locales/cs-CZ/grafana-prometheus.json
  46. 8
      packages/grafana-prometheus/src/locales/de-DE/grafana-prometheus.json
  47. 6
      packages/grafana-prometheus/src/locales/en-US/grafana-prometheus.json
  48. 8
      packages/grafana-prometheus/src/locales/es-ES/grafana-prometheus.json
  49. 8
      packages/grafana-prometheus/src/locales/fr-FR/grafana-prometheus.json
  50. 8
      packages/grafana-prometheus/src/locales/hu-HU/grafana-prometheus.json
  51. 8
      packages/grafana-prometheus/src/locales/id-ID/grafana-prometheus.json
  52. 8
      packages/grafana-prometheus/src/locales/it-IT/grafana-prometheus.json
  53. 8
      packages/grafana-prometheus/src/locales/ja-JP/grafana-prometheus.json
  54. 8
      packages/grafana-prometheus/src/locales/ko-KR/grafana-prometheus.json
  55. 8
      packages/grafana-prometheus/src/locales/nl-NL/grafana-prometheus.json
  56. 8
      packages/grafana-prometheus/src/locales/pl-PL/grafana-prometheus.json
  57. 8
      packages/grafana-prometheus/src/locales/pt-BR/grafana-prometheus.json
  58. 8
      packages/grafana-prometheus/src/locales/pt-PT/grafana-prometheus.json
  59. 8
      packages/grafana-prometheus/src/locales/ru-RU/grafana-prometheus.json
  60. 8
      packages/grafana-prometheus/src/locales/sv-SE/grafana-prometheus.json
  61. 8
      packages/grafana-prometheus/src/locales/tr-TR/grafana-prometheus.json
  62. 8
      packages/grafana-prometheus/src/locales/zh-Hans/grafana-prometheus.json
  63. 8
      packages/grafana-prometheus/src/locales/zh-Hant/grafana-prometheus.json
  64. 1
      packages/grafana-schema/src/common/common.gen.ts
  65. 4
      packages/grafana-schema/src/common/variables.cue
  66. 2
      packages/grafana-ui/package.json
  67. 11
      packages/grafana-ui/src/components/MatchersUI/FieldNameByRegexMatcherEditor.tsx
  68. 9
      packages/grafana-ui/src/components/MatchersUI/FieldNameMatcherEditor.tsx
  69. 5
      packages/grafana-ui/src/components/MatchersUI/FieldNamePicker.tsx
  70. 9
      packages/grafana-ui/src/components/MatchersUI/FieldNamesMatcherEditor.tsx
  71. 12
      packages/grafana-ui/src/components/MatchersUI/FieldTypeMatcherEditor.tsx
  72. 11
      packages/grafana-ui/src/components/MatchersUI/FieldValueMatcher.tsx
  73. 13
      packages/grafana-ui/src/components/MatchersUI/FieldsByFrameRefIdMatcher.tsx
  74. 24
      packages/grafana-ui/src/components/MatchersUI/fieldMatchersUI.ts
  75. 2
      packages/grafana-ui/src/components/VizTooltip/VizTooltipColorIndicator.tsx
  76. 12
      packages/grafana-ui/src/components/VizTooltip/VizTooltipRow.tsx
  77. 214
      packages/grafana-ui/src/components/uPlot/config.ts
  78. 2
      packages/grafana-ui/src/index.ts
  79. 77
      packages/grafana-ui/src/options/builder/axis.tsx
  80. 32
      packages/grafana-ui/src/options/builder/legend.tsx
  81. 5
      packages/grafana-ui/src/options/builder/stacking.tsx
  82. 20
      packages/grafana-ui/src/options/builder/text.tsx
  83. 36
      packages/grafana-ui/src/options/builder/tooltip.tsx
  84. 2
      packages/grafana-ui/src/themes/GlobalStyles/elements.ts
  85. 17
      pkg/aggregator/go.mod
  86. 28
      pkg/aggregator/go.sum
  87. 3
      pkg/apis/secret/go.mod
  88. 4
      pkg/apis/secret/go.sum
  89. 4
      pkg/apiserver/go.mod
  90. 8
      pkg/apiserver/go.sum
  91. 22
      pkg/build/a11y/README.md
  92. 79
      pkg/build/a11y/main.go
  93. 49
      pkg/build/a11y/run.go
  94. 60
      pkg/build/a11y/service.go
  95. 2
      pkg/build/go.mod
  96. 4
      pkg/build/go.sum
  97. 44
      pkg/cmd/grafana-cli/commands/datamigrations/to_unified_storage.go
  98. 2
      pkg/infra/httpclient/httpclientprovider/datasource_metrics_middleware.go
  99. 14
      pkg/promlib/go.mod
  100. 28
      pkg/promlib/go.sum
  101. Some files were not shown because too many files have changed in this diff Show More

@ -2627,9 +2627,6 @@ exports[`better eslint`] = {
"public/app/features/plugins/admin/components/GetStartedWithPlugin/index.ts:5381": [
[0, 0, 0, "Do not re-export imported variable (\`./GetStartedWithPlugin\`)", "0"]
],
"public/app/features/plugins/admin/components/InstallControls/InstallControlsWarning.tsx:5381": [
[0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"]
],
"public/app/features/plugins/admin/components/InstallControls/index.tsx:5381": [
[0, 0, 0, "Do not re-export imported variable (\`./InstallControlsButton\`)", "0"],
[0, 0, 0, "Do not re-export imported variable (\`./InstallControlsWarning\`)", "1"]

@ -393,13 +393,14 @@ steps:
- packages/grafana-ui/**
- commands:
- npx wait-on@7.0.1 http://$HOST:$PORT
- pa11y-ci --config .pa11yci-pr.conf.js
- pa11y-ci --config e2e/pa11yci.conf.js
depends_on:
- grafana-server
environment:
GRAFANA_MISC_STATS_API_KEY:
from_secret: grafana_misc_stats_api_key
HOST: grafana-server
NO_THRESHOLDS: "false"
PORT: 3001
failure: always
image: grafana/docker-puppeteer:1.1.0
@ -864,13 +865,14 @@ steps:
- packages/grafana-ui/**
- commands:
- npx wait-on@7.0.1 http://$HOST:$PORT
- pa11y-ci --config .pa11yci.conf.js --json > pa11y-ci-results.json
- pa11y-ci --config e2e/pa11yci.conf.js
depends_on:
- grafana-server
environment:
GRAFANA_MISC_STATS_API_KEY:
from_secret: grafana_misc_stats_api_key
HOST: grafana-server
NO_THRESHOLDS: "true"
PORT: 3001
failure: ignore
image: grafana/docker-puppeteer:1.1.0
@ -2984,6 +2986,6 @@ kind: secret
name: gcr_credentials
---
kind: signature
hmac: db5485c680a21c2f4032064cd7fd892a3075ebd92cd29422d892fcc375eb6948
hmac: 1198b1489e48a9ced211633a0325d112814553246847fc7320fb5ac2bcb32b7d
...

@ -646,8 +646,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
/scripts/**/generate-transformations* @grafana/datapro
/scripts/webpack/ @grafana/frontend-ops
/scripts/generate-a11y-report.sh @grafana/grafana-frontend-platform
.pa11yci.conf.js @grafana/grafana-frontend-platform
.pa11yci-pr.conf.js @grafana/grafana-frontend-platform
.betterer.results @grafanabot
.betterer.ts @grafana/grafana-frontend-platform
@ -664,7 +662,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
/public/app/plugins/datasource/graphite/ @grafana/partner-datasources
/public/app/plugins/datasource/influxdb/ @grafana/partner-datasources
/public/app/plugins/datasource/jaeger/ @grafana/oss-big-tent
/public/app/plugins/datasource/loki/ @grafana/oss-big-tent
/public/app/plugins/datasource/loki/ @grafana/oss-big-tent @grafana/observability-logs
/public/app/plugins/datasource/mixed/ @grafana/dashboards-squad
/public/app/plugins/datasource/mssql/ @grafana/partner-datasources
/public/app/plugins/datasource/mysql/ @grafana/oss-big-tent
@ -842,7 +840,8 @@ embed.go @grafana/grafana-as-code
/.github/workflows/trufflehog.yml @Proximyst
/.github/workflows/changelog.yml @zserge
/.github/workflows/shellcheck.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/release-build.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/release-build.yml @grafana/grafana-developer-enablement-squad
/.github/workflows/publish-artifact.yml @grafana/grafana-developer-enablement-squad
/.github/actions/changelog @zserge
/.github/workflows/swagger-gen.yml @grafana/grafana-backend-group
/.github/workflows/pr-frontend-unit-tests.yml @grafana/grafana-frontend-platform

@ -12,6 +12,8 @@ on:
pull_request:
paths:
- 'packages/**'
- '.nvmrc'
- '.github/workflows/detect-breaking-changes-levitate.yml'
branches:
- 'main'
@ -34,7 +36,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22.16.0
node-version-file: './pr/.nvmrc'
- name: Get yarn cache directory path
id: yarn-cache-dir-path
@ -86,7 +88,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22.16.0
node-version-file: './base/.nvmrc'
- name: Get yarn cache directory path
id: yarn-cache-dir-path
@ -136,7 +138,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22.16.0
node-version-file: '.nvmrc'
- name: Get built packages from pr
uses: actions/download-artifact@v4

@ -3,7 +3,7 @@ name: Run e2e for dashboardNewLayouts
on:
pull_request:
branches:
- '**'
- '**'
paths:
- 'e2e/dashboard-new-layouts/**'
- 'public/app/features/dashboard-scene/**'
@ -28,7 +28,7 @@ jobs:
- run: go version
- uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
run: yarn install --immutable

@ -35,7 +35,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22.16.0'
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies

@ -137,42 +137,33 @@ jobs:
path: videos
retention-days: 1
# Skipping this for now while we fix it
# run-a11y-test:
# needs:
# - build-grafana
# - build-e2e-runner
# name: A11y test
# runs-on: ubuntu-latest-8-cores
# permissions:
# contents: read
run-a11y-test:
needs:
- build-grafana
name: A11y test
runs-on: ubuntu-latest-8-cores
permissions:
contents: read
# steps:
# - uses: actions/checkout@v4
# with:
# persist-credentials: false
# - uses: actions/download-artifact@v4
# with:
# name: ${{ needs.build-grafana.outputs.artifact }}
# - uses: actions/download-artifact@v4
# with:
# name: ${{ needs.build-e2e-runner.outputs.artifact }}
# - name: chmod +x
# run: chmod +x ./e2e-runner
# - name: Run PR a11y test
# if: github.event_name == 'pull_request'
# uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
# with:
# verb: run
# args: go run ./pkg/build/a11y --package=grafana.tar.gz
# --flags="--json --config ./.pa11yci-pr.conf.js"
# - name: Run non-PR a11y test
# if: github.event_name != 'pull_request'
# uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
# with:
# verb: run
# args: go run ./pkg/build/a11y --package=grafana.tar.gz
# --flags="--json --config ./.pa11yci.conf.js"
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/download-artifact@v4
with:
name: ${{ needs.build-grafana.outputs.artifact }}
- name: Run PR a11y test
if: github.event_name == 'pull_request'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
verb: run
args: go run ./pkg/build/a11y --package=grafana.tar.gz
- name: Run non-PR a11y test
if: github.event_name != 'pull_request'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
verb: run
args: go run ./pkg/build/a11y --package=grafana.tar.gz --no-threshold-fail
# This is the job that is actually required by rulesets.
# We want to only require one job instead of all the individual tests.

@ -0,0 +1,67 @@
name: Publish artifacts to bucket
on:
workflow_call:
inputs:
pattern:
description: |
(From actinos/download-artifact) Glob pattern of artifacts (instead of `name`)
Be careful when using this option; the contents of the root of each artifact are coalesced, so ensure that they do not collide.
type: string
required: false
name:
description: (From actinos/download-artifact) Name of the GitHub artifact to upload (Ignored if `pattern` is set)
type: string
required: false
bucket:
description: Name of the GCS bucket
type: string
required: true
bucket-path:
description: Path in the GCS bucket
type: string
required: false
default: "."
environment:
description: "'prod' or 'dev'"
type: string
required: false
default: dev
run-id:
type: string
required: true
service-account:
type: string
required: false
default: github-prerelease-writer@grafanalabs-workload-identity.iam.gserviceaccount.com
jobs:
publish:
runs-on: github-hosted-ubuntu-x64-small
name: Publish
permissions:
id-token: write
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: ${{ inputs.name }}
pattern: ${{ inputs.pattern }}
run-id: ${{ inputs.run-id }}
path: ./artifact
- name: Log in to GCS
id: login-to-gcs
uses: grafana/shared-workflows/actions/login-to-gcs@login-to-gcs/v0.2.1
with:
environment: ${{ inputs.environment }}
service_account: ${{ inputs.service-account }}
- name: Coalesce artifacts
run: |
mkdir out
find ./artifact -mindepth 2 -maxdepth 2 -exec cp -r {} out/ \;
ls -al out
- name: Upload artifacts
uses: grafana/shared-workflows/actions/push-to-gcs@push-to-gcs-v0.2.0
with:
bucket: ${{ inputs.bucket }}
environment: ${{ inputs.environment }}
parent: false
path: out
bucket_path: ${{ inputs.bucket-path }}

@ -8,7 +8,7 @@ on:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
cancel-in-progress: false
permissions:
contents: read
@ -64,6 +64,55 @@ jobs:
BUILD_ID: ${{ github.run_number }}
- id: output
run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
# Triggers the same workflow in `grafana-enterprise` on the same ref
downstream:
runs-on: github-hosted-ubuntu-x64-small
needs: [setup]
permissions:
contents: read
id-token: write
name: Dispatch grafana-enterprise build
steps:
- name: Log in to GCS
id: login-to-gcs
uses: grafana/shared-workflows/actions/login-to-gcs@login-to-gcs/v0.2.1
with:
environment: prod
- id: vault-secrets
uses: grafana/shared-workflows/actions/get-vault-secrets@main
with:
repo_secrets: |
GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
with:
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
repositories: '["grafana-enterprise"]'
permissions: '{"actions": "write"}'
- uses: actions/github-script@v7
env:
REF: ${{ github.ref_name }}
VERSION: ${{ needs.setup.outputs.version }}
BUILD_ID: ${{ github.run_number }}
BUCKET: ${{ steps.login-to-gcs.outputs.bucket }}
with:
script: |
const {REF, VERSION, BUILD_ID, BUCKET} = process.env;
await github.rest.actions.createWorkflowDispatch({
owner: 'grafana',
repo: 'grafana-enterprise',
workflow_id: 'release-build.yml',
ref: REF,
inputs: {
"version": VERSION,
"build-id": BUILD_ID,
"bucket": BUCKET,
}
})
build:
runs-on: github-hosted-ubuntu-x64-large
needs: [setup]
@ -72,6 +121,7 @@ jobs:
id-token: write
name: ${{ needs.setup.outputs.version }} / ${{ matrix.name }}
strategy:
fail-fast: false
matrix:
# The artifacts in these lists are grouped by their os+arch because the
# build process can reuse the binaries for each artifact.
@ -97,6 +147,7 @@ jobs:
- name: darwin-arm64
artifacts: targz:grafana:darwin/arm64
steps:
- uses: grafana/shared-workflows/actions/dockerhub-login@main
- uses: actions/checkout@v4
with:
persist-credentials: false
@ -115,23 +166,41 @@ jobs:
output: artifacts-${{ matrix.name }}.txt
verify: true
build-id: ${{ github.run_number }}
- name: Log in to GCS
id: login-to-gcs
uses: grafana/shared-workflows/actions/login-to-gcs@login-to-gcs/v0.2.1
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
environment: prod
- name: Upload artifacts
uses: grafana/shared-workflows/actions/push-to-gcs@push-to-gcs-v0.2.0
name: artifacts-list-${{ matrix.name }}
path: ${{ steps.build.outputs.file }}
retention-days: 1
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
with:
bucket: ${{ steps.login-to-gcs.outputs.bucket }}
environment: prod
parent: false
name: artifacts-${{ matrix.name }}
path: ${{ steps.build.outputs.dist-dir }}
bucket_path: ${{ needs.setup.outputs.version }}
- name: Upload manifest
uses: grafana/shared-workflows/actions/push-to-gcs@push-to-gcs-v0.2.0
with:
bucket: ${{ steps.login-to-gcs.outputs.bucket }}
environment: prod
path: ${{ steps.build.outputs.file }}
bucket_path: ${{ needs.setup.outputs.version }}
retention-days: 1
publish-artifacts:
name: Upload artifacts
uses: grafana/grafana/.github/workflows/publish-artifact.yml@main
permissions:
id-token: write
needs:
- setup
- build
with:
bucket: grafana-prerelease
pattern: artifacts-*
run-id: ${{ github.run_id }}
bucket-path: ${{ needs.setup.outputs.version }}
environment: prod
publish-artifact-lists:
name: Upload artifacts
uses: grafana/grafana/.github/workflows/publish-artifact.yml@main
permissions:
id-token: write
needs:
- setup
- build
with:
bucket: grafana-prerelease
pattern: artifacts-list-*
run-id: ${{ github.run_id }}
bucket-path: ${{ needs.setup.outputs.version }}
environment: prod

@ -36,7 +36,7 @@ jobs:
- run: go version
- uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Cache Node Modules
id: cache-node-modules

@ -6,7 +6,7 @@ on:
- main
pull_request:
branches:
- '**'
- '**'
env:
ARCH: linux-amd64
@ -28,7 +28,7 @@ jobs:
- run: go version
- uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies
run: yarn install --immutable
@ -40,7 +40,7 @@ jobs:
runTests: false
- name: Run dashboard scenes e2e
run: yarn e2e:schema-v2 || echo "Test failed but marking as success since schema V2 is behind a feature flag and should not block PRs"
- name: Always succeed # This is a workaround to make the job pass even if the previous step fails
if: failure()
run: exit 0

@ -32,7 +32,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Install dependencies

1
.gitignore vendored

@ -69,6 +69,7 @@ public/css/*.min.css
!.vscode/launch.json
.vs/
.cursor/
.devcontainer/
.eslintcache
.stylelintcache

@ -1,140 +0,0 @@
var dashboardSettings = [
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=settings',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=annotations',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=templating',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=links',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=versions',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=permissions',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=dashboard_json',
wait: 500,
rootElement: '.main-view',
},
];
var config = {
defaults: {
concurrency: 1,
runners: ['axe'],
useIncognitoBrowserContext: false,
standard: 'WCAG2AA',
chromeLaunchConfig: {
args: ['--no-sandbox'],
},
// see https://github.com/grafana/grafana/pull/41693#issuecomment-979921463 for context
// on why we're ignoring singleValue/react-select-*-placeholder elements
hideElements: '#updateVersion, [class*="-singleValue"], [id^="react-select-"][id$="-placeholder"]',
},
urls: [
{
url: '${HOST}/login',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/login', //skip password and login
actions: [
"wait for element input[name='user'] to be added",
"set field input[name='user'] to admin",
"set field input[name='password'] to admin",
"click element button[data-testid='data-testid Login button']",
"wait for element button[data-testid='data-testid Skip change password button'] to be visible",
],
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/?orgId=1',
wait: 500,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge',
wait: 500,
rootElement: '.main-view',
},
...dashboardSettings,
{
url: '${HOST}/?orgId=1&search=open',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/alerting/list',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/datasources',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/org/users',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/org/teams',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/plugins',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/org',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/org/apikeys',
wait: 500,
rootElement: '.main-view',
},
{
url: '${HOST}/dashboards',
wait: 500,
rootElement: '.main-view',
},
],
};
function myPa11yCiConfiguration(urls, defaults) {
const HOST_SERVER = process.env.HOST || 'localhost';
const PORT_SERVER = process.env.PORT || '3001';
for (var idx = 0; idx < urls.length; idx++) {
urls[idx] = { ...urls[idx], url: urls[idx].url.replace('${HOST}', `${HOST_SERVER}:${PORT_SERVER}`) };
}
return {
defaults: defaults,
urls: urls,
};
}
module.exports = myPa11yCiConfiguration(config.urls, config.defaults);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -25,4 +25,4 @@ plugins:
path: .yarn/plugins/@yarnpkg/plugin-licenses.cjs
spec: "https://raw.githubusercontent.com/mhassan1/yarn-plugin-licenses/v0.15.0/bundles/@yarnpkg/plugin-licenses.js"
yarnPath: .yarn/releases/yarn-4.6.0.cjs
yarnPath: .yarn/releases/yarn-4.9.2.cjs

@ -85,7 +85,7 @@ func New(cfg app.Config, log logging.Logger) (app.Runnable, error) {
func (r *Runner) Run(ctx context.Context) error {
logger := r.log.WithContext(ctx)
lastCreated, err := r.checkLastCreated(ctx, logger)
lastCreated, err := r.checkLastCreated(context.WithoutCancel(ctx), logger)
if err != nil {
logger.Error("Error getting last check creation time", "error", err)
// Wait for interval to create the next scheduled check
@ -93,7 +93,7 @@ func (r *Runner) Run(ctx context.Context) error {
} else {
// do an initial creation if necessary
if lastCreated.IsZero() {
err = r.createChecks(ctx, logger)
err = r.createChecks(context.WithoutCancel(ctx), logger)
if err != nil {
logger.Error("Error creating new check reports", "error", err)
} else {
@ -109,12 +109,12 @@ func (r *Runner) Run(ctx context.Context) error {
for {
select {
case <-ticker.C:
err = r.createChecks(ctx, logger)
err = r.createChecks(context.WithoutCancel(ctx), logger)
if err != nil {
logger.Error("Error creating new check reports", "error", err)
}
err = r.cleanupChecks(ctx, logger)
err = r.cleanupChecks(context.WithoutCancel(ctx), logger)
if err != nil {
logger.Error("Error cleaning up old check reports", "error", err)
}
@ -147,7 +147,7 @@ func (r *Runner) checkLastCreated(ctx context.Context, log logging.Logger) (time
// If the check is unprocessed, set it to error
if checks.GetStatusAnnotation(item) == "" {
log.Error("Check is unprocessed", "check", item.GetStaticMetadata().Identifier())
log.Info("Check is unprocessed, marking as error", "check", item.GetStaticMetadata().Identifier())
err := checks.SetStatusAnnotation(ctx, r.client, item, checks.StatusAnnotationError)
if err != nil {
log.Error("Error setting check status to error", "error", err)
@ -168,7 +168,7 @@ func (r *Runner) createChecks(ctx context.Context, logger logging.Logger) error
allChecksRegistered := len(list.GetItems()) == len(r.checkRegistry.Checks())
retryCount := 0
for !allChecksRegistered && retryCount < waitMaxRetries {
logger.Error("Waiting for all check types to be registered", "retryCount", retryCount, "waitInterval", waitInterval)
logger.Info("Waiting for all check types to be registered", "retryCount", retryCount, "waitInterval", waitInterval)
time.Sleep(waitInterval)
list, err = r.typesClient.List(ctx, r.namespace, resource.ListOptions{})
if err != nil {

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"maps"
"strings"
"time"
"github.com/grafana/grafana-app-sdk/app"
@ -81,7 +82,7 @@ func (r *Runner) createOrUpdate(ctx context.Context, log logging.Logger, obj res
_, err = r.client.Update(ctx, id, obj, resource.UpdateOptions{})
if err != nil && !errors.IsAlreadyExists(err) {
// Ignore the error, it's probably due to a race condition
log.Error("Error updating check type", "error", err)
log.Info("Error updating check type, ignoring", "error", err)
}
return nil
}
@ -123,9 +124,13 @@ func (r *Runner) Run(ctx context.Context) error {
for i := 0; i < r.retryAttempts; i++ {
err := r.createOrUpdate(context.WithoutCancel(ctx), logger, obj)
if err != nil {
logger.Error("Error creating check type, retrying", "error", err, "attempt", i+1)
if strings.Contains(err.Error(), "apiserver is shutting down") {
logger.Debug("Error creating check type, not retrying", "error", err)
return nil
}
logger.Debug("Error creating check type, retrying", "error", err, "attempt", i+1)
if i == r.retryAttempts-1 {
logger.Error("Unable to register check type")
logger.Error("Unable to register check type", "check_type", t.ID(), "error", err)
} else {
// Calculate exponential backoff delay: baseDelay * 2^attempt
delay := r.retryDelay * time.Duration(1<<i)

@ -123,6 +123,24 @@ func TestCheckTypesRegisterer_Run(t *testing.T) {
},
expectedErr: errors.New("update error"),
},
{
name: "shutting down error",
checks: []checks.Check{
&mockCheck{
id: "check1",
steps: []checks.Step{
&mockStep{id: "step1", title: "Step 1", description: "Description 1"},
},
},
},
createFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.CreateOptions) (resource.Object, error) {
return nil, k8sErrs.NewAlreadyExists(schema.GroupResource{}, obj.GetName())
},
updateFunc: func(ctx context.Context, id resource.Identifier, obj resource.Object, opts resource.UpdateOptions) (resource.Object, error) {
return nil, errors.New("apiserver is shutting down")
},
expectedErr: nil,
},
{
name: "custom namespace",
checks: []checks.Check{

@ -53,7 +53,7 @@ func processCheck(ctx context.Context, log logging.Logger, client resource.Clien
if setErr != nil {
return setErr
}
return fmt.Errorf("error initializing check: %w", err)
return fmt.Errorf("error listing items for check: %w", err)
}
// Get the check type
var checkType resource.Object
@ -130,7 +130,7 @@ func processCheckRetry(ctx context.Context, log logging.Logger, client resource.
if setErr != nil {
return setErr
}
return fmt.Errorf("error initializing check: %w", err)
return fmt.Errorf("error getting item for check: %w", err)
}
if item != nil {
// Get the check type
@ -206,7 +206,7 @@ func runStepsInParallel(ctx context.Context, log logging.Logger, spec *advisorv0
func() {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic recovered in step %s: %v", step.ID(), r)
log.Error("panic recovered in step", "step", step.ID(), "error", r, "item", item)
}
}()
logger := log.With("step", step.ID())

@ -178,9 +178,8 @@ func TestProcessCheck_RunRecoversFromPanic(t *testing.T) {
}
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
assert.Error(t, err)
assert.Contains(t, err.Error(), "panic recovered in step")
assert.Equal(t, checks.StatusAnnotationError, obj.GetAnnotations()[checks.StatusAnnotation])
assert.NoError(t, err)
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
}
func TestProcessCheckRetry_NoRetry(t *testing.T) {

@ -70,14 +70,13 @@ require (
go.etcd.io/etcd/client/v3 v3.5.21 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect

@ -215,8 +215,8 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0=

@ -5,7 +5,7 @@ go 1.24.4
require (
cuelang.org/go v0.11.1
github.com/grafana/grafana-app-sdk v0.39.0
github.com/grafana/grafana-plugin-sdk-go v0.277.0
github.com/grafana/grafana-plugin-sdk-go v0.278.0
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e
github.com/stretchr/testify v1.10.0
k8s.io/apimachinery v0.33.1
@ -14,12 +14,12 @@ require (
require (
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/apache/arrow-go/v18 v18.2.0 // indirect
github.com/apache/arrow-go/v18 v18.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 // indirect
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@ -39,7 +39,6 @@ require (
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect
@ -48,7 +47,7 @@ require (
github.com/grafana/grafana-app-sdk/logging v0.38.2 // indirect
github.com/grafana/otel-profiling-go v0.5.1 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
@ -95,8 +94,8 @@ require (
github.com/zeebo/xxh3 v1.0.2 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
@ -117,7 +116,6 @@ require (
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.34.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
google.golang.org/grpc v1.73.0 // indirect

@ -8,8 +8,8 @@ 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/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/apache/arrow-go/v18 v18.2.0 h1:QhWqpgZMKfWOniGPhbUxrHohWnooGURqL2R2Gg4SO1Q=
github.com/apache/arrow-go/v18 v18.2.0/go.mod h1:Ic/01WSwGJWRrdAZcxjBZ5hbApNJ28K96jGYaxzzGUc=
github.com/apache/arrow-go/v18 v18.3.0 h1:Xq4A6dZj9Nu33sqZibzn012LNnewkTUlfKVUFD/RX/I=
github.com/apache/arrow-go/v18 v18.3.0/go.mod h1:eEM1DnUTHhgGAjf/ChvOAQbUQ+EPohtDrArffvUjPg8=
github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE=
github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -22,8 +22,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 h1:VnjHsRXCRti7Av7E+j4DCha3kf68echfDzQ+wD11SBU=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 h1:UZdrvid2JFwnvPlUSEFlE794XZL4Jmrj8fuxfcLECJE=
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@ -98,16 +98,16 @@ github.com/grafana/grafana-app-sdk v0.39.0 h1:WC2E9BKXWDX/e2bajdAFjQEyyWf9BFp7Yz
github.com/grafana/grafana-app-sdk v0.39.0/go.mod h1:xRyBQOttgWTc3tGe9pI0upnpEPVhzALf7Mh/61O4zyY=
github.com/grafana/grafana-app-sdk/logging v0.38.2 h1:EdQTRxbbH72zdqJ09Z76zcSjfALJXkpPLgvKEPPnloc=
github.com/grafana/grafana-app-sdk/logging v0.38.2/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
github.com/grafana/grafana-plugin-sdk-go v0.277.0 h1:VDU2F4Y5NeRS//ejctdZtsAshrGaEdbtW33FsK0EQss=
github.com/grafana/grafana-plugin-sdk-go v0.277.0/go.mod h1:mAUWg68w5+1f5TLDqagIr8sWr1RT9h7ufJl5NMcWJAU=
github.com/grafana/grafana-plugin-sdk-go v0.278.0 h1:5/rIYparLi02pofdaag8wnjspMMVNCi8cZhC4cdC3Ho=
github.com/grafana/grafana-plugin-sdk-go v0.278.0/go.mod h1:+8NXT/XUJ/89GV6FxGQ366NZ3nU+cAXDMd0OUESF9H4=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:IA4SOwun8QyST9c5UNs/fN37XL6boXXDvRYFcFwbipg=
github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8=
github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
@ -268,12 +268,12 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 h1:lREC4C0ilyP4WibDhQ7Gg2ygAQFP8oR07Fst/5cafwI=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0/go.mod h1:HfvuU0kW9HewH14VCOLImqKvUgONodURG7Alj/IrnGI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 h1:SoCgXYF4ISDtNyfLUzsGDaaudZVTx2yJhOyBO0+/GYk=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0/go.mod h1:VHu48l0YTRKSObdPQ+Sb8xMZvdnJlN7yhHuHoPgNqHM=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 h1:bQ1Gvah4Sp8z7epSkgJaNTuZm7sutfA6Fji2/7cKFMc=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0/go.mod h1:9b8Q9rH52NgYH3ShiTFB5wf18Vt3RTH/VMB7LDcC1ug=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=

@ -986,6 +986,101 @@
],
"title": "special null + NaN value mapping from data",
"type": "state-timeline"
},
{
"datasource": {
"type": "testdata"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisPlacement": "auto",
"fillOpacity": 70,
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineWidth": 0,
"spanNulls": false
},
"fieldMinMax": false,
"mappings": [
{
"options": {
"match": "false",
"result": {
"color": "red",
"index": 0
}
},
"type": "special"
},
{
"options": {
"match": "null+nan",
"result": {
"color": "blue",
"index": 1,
"text": "null + NaN"
}
},
"type": "special"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 6,
"x": 12,
"y": 18
},
"id": 14,
"options": {
"alignValue": "center",
"legend": {
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"mergeValues": true,
"rowHeight": 0.9,
"showValue": "auto",
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.1.0-pre",
"targets": [
{
"datasource": {
"type": "testdata",
"uid": "PD8C576611E62080A"
},
"rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"fields\": [\n {\n \"name\": \"time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"int64\",\n \"nullable\": true\n },\n \"config\": {\n \"thresholds\": {\n \"mode\": \"absolute\",\n \"steps\": [\n {\n \"color\": \"green\",\n \"value\": null\n }\n ]\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1674732835000,\n 1674736435000,\n 1674740035000,\n 1674743635000,\n 1674747235000,\n 1674750835000,\n 1674754235000,\n 1674757835000\n ],\n [\n null,\n null,\n false,\n true,\n true,\n false,\n true,\n null\n ]\n ],\n \"entities\": [null, { \"NaN\": [0], \"Undefined\": [1]}]\n }\n }\n]",
"refId": "A",
"scenarioId": "raw_frame"
}
],
"title": "boolean values from data",
"type": "state-timeline"
}
],
"preload": false,
@ -1008,5 +1103,5 @@
"timezone": "utc",
"title": "StateTimeline - Thresholds & Mappings",
"uid": "Kce7z9TVz",
"version": 13
}
"version": 18
}

@ -54,13 +54,14 @@ The following tables list permissions associated with basic and fixed roles. Thi
## Basic role assignments
| Basic role | UID | Associated fixed roles | Description |
| ------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Grafana Admin | `basic_grafana_admin` | fixed:roles:writer`<br>`fixed:users:writer`<br>`fixed:org.users:writer`<br>`fixed:ldap:writer`<br>`fixed:stats:reader`<br>`fixed:settings:writer`<br>`fixed:provisioning:writer`<br>`fixed:organization:maintainer`<br>`fixed:licensing:writer`<br>`fixed:plugins:maintainer`<br>`fixed:authentication.config:writer`<br>`fixed:migrationassistant:migrator` | Default [Grafana server administrator](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/#grafana-server-administrators) assignments. |
| Admin | `basic_admin` | All roles assigned to Editor and `fixed:reports:writer` <br>`fixed:datasources:writer`<br>`fixed:organization:writer`<br>`fixed:datasources.permissions:writer`<br>`fixed:teams:writer`<br>`fixed:dashboards:writer`<br>`fixed:dashboards.permissions:writer`<br>`fixed:dashboards.public:writer`<br>`fixed:folders:writer`<br>`fixed:folders.permissions:writer`<br>`fixed:alerting:writer`<br>`fixed:apikeys:writer`<br>`fixed:alerting.provisioning.secrets:reader`<br>`fixed:alerting.provisioning:writer`<br>`fixed:datasources.caching:writer`<br>`fixed:plugins:writer`<br>`fixed:library.panels:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. |
| Editor | `basic_editor` | All roles assigned to Viewer and `fixed:datasources:explorer` <br>`fixed:dashboards:creator`<br>`fixed:folders:creator`<br>`fixed:annotations:writer`<br>`fixed:alerting:writer`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:general.writer`<br>`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
| Viewer | `basic_viewer` | `fixed:datasources.id:reader`<br>`fixed:organization:reader`<br>`fixed:annotations:reader`<br>`fixed:annotations.dashboard:writer`<br>`fixed:alerting:reader`<br>`fixed:plugins.app:reader`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:folders.general:reader`<br>`fixed:datasources.builtin:reader` | Default [Viewer](ref:rbac-basic-roles) assignments. |
| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) |
| Basic role | UID | Associated fixed roles | Description |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| Grafana Admin | `basic_grafana_admin` |
| `fixed:authentication.config:writer`<br>`fixed:general.auth.config:writer`<br>`fixed:ldap:writer`<br>`fixed:licensing:writer`<br>`fixed:migrationassistant:migrator`<br>`fixed:org.users:writer`<br>`fixed:organization:maintainer`<br>`fixed:plugins:maintainer`<br>`fixed:provisioning:writer`<br>`fixed:roles:writer`<br>`fixed:settings:reader`<br>`fixed:settings:writer`<br>`fixed:stats:reader`<br>`fixed:support.bundles:writer`<br>`fixed:usagestats:reader`<br>`fixed:users:writer` | Default [Grafana server administrator](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/#grafana-server-administrators) assignments. |
| Admin | `basic_admin` | All roles assigned to Editor and `fixed:reports:writer` <br>`fixed:datasources:writer`<br>`fixed:organization:writer`<br>`fixed:datasources.permissions:writer`<br>`fixed:teams:writer`<br>`fixed:dashboards:writer`<br>`fixed:dashboards.permissions:writer`<br>`fixed:dashboards.public:writer`<br>`fixed:folders:writer`<br>`fixed:folders.permissions:writer`<br>`fixed:alerting:writer`<br>`fixed:apikeys:writer`<br>`fixed:alerting.provisioning.secrets:reader`<br>`fixed:alerting.provisioning:writer`<br>`fixed:datasources.caching:writer`<br>`fixed:plugins:writer`<br>`fixed:library.panels:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. |
| Editor | `basic_editor` | All roles assigned to Viewer and `fixed:datasources:explorer` <br>`fixed:dashboards:creator`<br>`fixed:folders:creator`<br>`fixed:annotations:writer`<br>`fixed:alerting:writer`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:general.writer`<br>`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
| Viewer | `basic_viewer` | `fixed:datasources.id:reader`<br>`fixed:organization:reader`<br>`fixed:annotations:reader`<br>`fixed:annotations.dashboard:writer`<br>`fixed:alerting:reader`<br>`fixed:plugins.app:reader`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:folders.general:reader`<br>`fixed:datasources.builtin:reader` | Default [Viewer](ref:rbac-basic-roles) assignments. |
| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) |
## Fixed role definitions
@ -92,6 +93,7 @@ To learn how to use the roles API to determine the role UUIDs, refer to [Manage
| `fixed:apikeys:reader` | `fixed_kYZ7UEkwEvGmCCjTrq07cFAVFws` | `apikeys:read` for scope `apikeys:*` | Read all api keys. |
| `fixed:apikeys:writer` | `fixed_anTrcpRkm21NBO1Q2CsX8y0fiCQ` | All permissions from `fixed:apikeys:reader` and <br> `apikeys:create` <br> `apikeys:delete` for scope `apikeys:*` | Read, create, delete all api keys. |
| `fixed:authentication.config:writer` | `fixed_0rYhZ2Qnzs8AdB1nX7gexk3fHDw` | `settings:read` for scope `settings:auth.saml:*` <br> `settings:write` for scope `settings:auth.saml:*` | Read and update authentication and SAML settings. |
| `fixed:general.auth.config:writer` | `fixed_QFxIT_FGtBqbIVJIwx1bLgI5z6c` | `settings:read` for scope `settings:auth:oauth_allow_insecure_email_lookup` <br> `settings:write` for scope `settings:auth:oauth_allow_insecure_email_lookup` | Read and update the Grafana instance's general authentication configuration settings. |
| `fixed:dashboards:creator` | `fixed_ZorKUcEPCM01A1fPakEzGBUyU64` | `dashboards:create`<br>`folders:read` | Create dashboards. |
| `fixed:dashboards:reader` | `fixed_Sgr67JTOhjQGFlzYRahOe45TdWM` | `dashboards:read` | Read all dashboards. |
| `fixed:dashboards:writer` | `fixed_OK2YOQGIoI1G031hVzJB6rAJQAs` | All permissions from `fixed:dashboards:reader` and <br>`dashboards:write`<br>`dashboards:delete`<br>`dashboards:create`<br>`dashboards.permissions:read`<br>`dashboards.permissions:write` | Read, create, update, and delete all dashboards. |
@ -146,10 +148,13 @@ To learn how to use the roles API to determine the role UUIDs, refer to [Manage
| `fixed:settings:reader` | `fixed_0LaUt1x6PP8hsZzEBhqPQZFUd8Q` | `settings:read` | Read Grafana instance settings. |
| `fixed:settings:writer` | `fixed_joIHDgMrGg790hMhUufVzcU4j44` | All permissions from `fixed:settings:reader` and<br>`settings:write` | Read and update Grafana instance settings. |
| `fixed:stats:reader` | `fixed_OnRCXxZVINWpcKvTF5A1gecJ7pA` | `server.stats:read` | Read Grafana instance statistics. |
| `fixed:support.bundles:reader` | `fixed_gcPjI3PTUJwRx-GJZwDhNa7zbos` | `support.bundles:read` | List and download support bundles. |
| `fixed:support.bundles:writer` | `fixed_dTgCv9Wxrp_WHAhwHYIgeboxKpE` | `support.bundles:read`<br>`support.bundles:create`<br>`support.bundles:delete` | Create, delete, list and download support bundles. |
| `fixed:teams:creator` | `fixed_nzVQoNSDSn0fg1MDgO6XnZX2RZI` | `teams:create`<br>`org.users:read` | Create a team and list organization users (required to manage the created team). |
| `fixed:teams:read` | `fixed_Z8pB0GQlrqRt8IZBCJQxPWvJPgQ` | `teams:read` | List all teams. |
| `fixed:teams:writer` | `fixed_xw1T0579h620MOYi4L96GUs7fZY` | `teams:create`<br>`teams:delete`<br>`teams:read`<br>`teams:write`<br>`teams.permissions:read`<br>`teams.permissions:write` | Create, read, update and delete teams and manage team memberships. |
| `fixed:users:reader` | `fixed_buZastUG3reWyQpPemcWjGqPAd0` | `users:read`<br>`users.quotas:read`<br>`users.authtoken:read`<br>` | Read all users and their information, such as team memberships, authentication tokens, and quotas. |
| `fixed:usagestats:reader` | `fixed_eAM0azEvnWFCJAjNkUKnGL_1-bU` | `server.usagestats.report:read` | View usage statistics report. |
| `fixed:users:reader` | `fixed_buZastUG3reWyQpPemcWjGqPAd0` | `users:read`<br>`users.quotas:read`<br>`users.authtoken:read` | Read all users and their information, such as team memberships, authentication tokens, and quotas. |
| `fixed:users:writer` | `fixed_wjzgHHo_Ux25DJuELn_oiAdB_yM` | All permissions from `fixed:users:reader` and <br>`users:write`<br>`users:create`<br>`users:delete`<br>`users:enable`<br>`users:disable`<br>`users.password:write`<br>`users.permissions:write`<br>`users:logout`<br>`users.authtoken:write`<br>`users.quotas:write` | Read and update all attributes and settings for all users in Grafana: update user information, read user information, create or enable or disable a user, make a user a Grafana administrator, sign out a user, update a user’s authentication token, or update quotas for all users. |
### Alerting roles

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

@ -21,8 +21,19 @@ refs:
# Reporting settings
You can configure organization-wide report settings and branding options in **Dashboards > Reporting > Settings**.
Settings are applied to all the reports for the current organization.
You can configure organization-wide report settings and branding options in **Dashboards > Reporting**.
These settings are applied to all the reports for the current organization.
To access the settings, go to **Dashboards > Reporting** and click the **Report settings** button.
This opens the **Report template settings** drawer, where you can make changes.
{{< admonition type="note" >}}
The redesigned reporting feature, including the report settings drawer, is currently in public preview. Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available. To use this feature, enable the `newShareReportDrawer` feature toggle in your Grafana configuration file or, for Grafana Cloud, contact Support.
{{< /admonition >}}
You can also navigate these settings from the **Schedule report** drawer that opens when you create a report directly from a dashboard.
## Attachment settings

@ -390,8 +390,8 @@ The database user's password (not applicable for `sqlite3`). If the password con
#### `url`
Use either URL or the other fields below to configure the database
Example: `mysql://user:secret@host:port/database`
Use either URL or the previous fields to configure the database
Example: `type://user:password@host:port/name`
#### `max_idle_conn`

@ -85,6 +85,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `alertingMigrationUI` | Enables the alerting migration UI, to migrate data source-managed rules to Grafana-managed rules | Yes |
| `alertingImportYAMLUI` | Enables a UI feature for importing rules from a Prometheus file to Grafana-managed rules | Yes |
| `unifiedNavbars` | Enables unified navbars | |
| `tabularNumbers` | Use fixed-width numbers globally in the UI | Yes |
## Public preview feature toggles

@ -118,7 +118,7 @@ If set to `true`, the Grafana server hides the running version number for unauth
Example:
```toml
[anonymous.auth]
[auth.anonymous]
# mask the Grafana version number for unauthenticated users
hide_version = true
```

@ -14,6 +14,8 @@ describe('Panels smokescreen', () => {
it('Tests each panel type in the panel edit view to ensure no crash', () => {
e2e.flows.addDashboard();
e2e.pages.Dashboard.DashNav.shareButton().should('be.visible');
e2e.flows.addPanel({
dataSourceName: 'gdev-testdata',
timeout: 10000,

@ -1,50 +1,4 @@
var dashboardSettings = [
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=settings',
wait: 500,
rootElement: '.main-view',
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=annotations',
wait: 500,
rootElement: '.main-view',
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=templating',
wait: 500,
rootElement: '.main-view',
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=links',
wait: 500,
rootElement: '.main-view',
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=versions',
wait: 500,
rootElement: '.main-view',
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=permissions',
wait: 500,
rootElement: '.main-view',
// TODO: improve the accessibility of the permission tab https://github.com/grafana/grafana/issues/77203
threshold: 5,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=dashboard_json',
wait: 500,
rootElement: '.main-view',
threshold: 2,
},
];
var config = {
const config = {
defaults: {
concurrency: 1,
runners: ['axe'],
@ -57,18 +11,16 @@ var config = {
// see https://github.com/grafana/grafana/pull/41693#issuecomment-979921463 for context
// on why we're ignoring singleValue/react-select-*-placeholder elements
hideElements: '#updateVersion, [class*="-singleValue"], [id^="react-select-"][id$="-placeholder"]',
reporters: ['cli', ['json', { fileName: './pa11y-ci-results.json' }]],
},
urls: [
{
url: '${HOST}/login',
wait: 500,
rootElement: '.main-view',
threshold: 0,
},
{
url: '${HOST}/login',
wait: 500,
actions: [
"wait for element input[name='user'] to be added",
"set field input[name='user'] to admin",
@ -77,74 +29,84 @@ var config = {
"wait for element button[data-testid='data-testid Skip change password button'] to be visible",
],
threshold: 2,
rootElement: '.main-view',
},
{
url: '${HOST}/?orgId=1',
wait: 500,
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge',
wait: 500,
rootElement: '.main-view',
threshold: 0,
},
...dashboardSettings,
// Dashboard settings
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=settings',
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=annotations',
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=variables',
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=links',
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=versions',
threshold: 0,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=permissions',
// TODO: improve the accessibility of the permission tab https://github.com/grafana/grafana/issues/77203
threshold: 5,
},
{
url: '${HOST}/d/O6f11TZWk/panel-tests-bar-gauge?orgId=1&editview=dashboard_json',
threshold: 2,
},
// Misc
{
url: '${HOST}/?orgId=1&search=open',
wait: 500,
rootElement: '.main-view',
threshold: 0,
},
{
url: '${HOST}/alerting/list',
wait: 500,
rootElement: '.main-view',
// the unified alerting promotion alert's content contrast is too low
// see https://github.com/grafana/grafana/pull/41829
threshold: 7,
},
{
url: '${HOST}/datasources',
wait: 500,
rootElement: '.main-view',
threshold: 0,
},
{
url: '${HOST}/org/users',
wait: 500,
rootElement: '.main-view',
threshold: 2,
},
{
url: '${HOST}/org/teams',
wait: 500,
rootElement: '.main-view',
threshold: 0,
},
{
url: '${HOST}/plugins',
wait: 500,
rootElement: '.main-view',
threshold: 0,
},
{
url: '${HOST}/org',
wait: 500,
rootElement: '.main-view',
threshold: 2,
},
{
url: '${HOST}/org/apikeys',
wait: 500,
rootElement: '.main-view',
threshold: 4,
},
{
url: '${HOST}/dashboards',
wait: 500,
rootElement: '.main-view',
threshold: 2,
},
],
@ -153,9 +115,21 @@ var config = {
function myPa11yCiConfiguration(urls, defaults) {
const HOST_SERVER = process.env.HOST || 'localhost';
const PORT_SERVER = process.env.PORT || '3001';
for (var idx = 0; idx < urls.length; idx++) {
urls[idx] = { ...urls[idx], url: urls[idx].url.replace('${HOST}', `${HOST_SERVER}:${PORT_SERVER}`) };
}
const noThresholds = process.env.NO_THRESHOLDS === 'true';
urls = urls.map((test, index) => {
return {
...test,
url: test.url.replace('${HOST}', `${HOST_SERVER}:${PORT_SERVER}`),
screenCapture: `./screenshots/screenshot-${index}.png`,
rootElement: '.main-view',
wait: 500,
// Depending on NO_THRESHOLDS (--no-threshold-fail in the dagger command), clear the thresholds
// to allow pa11y to fail the check and include error details in the results file
threshold: noThresholds ? undefined : test.threshold,
};
});
return {
defaults: defaults,

@ -28,7 +28,7 @@ require (
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f // @grafana/grafana-backend-group
github.com/alicebob/miniredis/v2 v2.34.0 // @grafana/alerting-backend
github.com/andybalholm/brotli v1.1.1 // @grafana/partner-datasources
github.com/apache/arrow-go/v18 v18.2.0 // @grafana/plugins-platform-backend
github.com/apache/arrow-go/v18 v18.3.0 // @grafana/plugins-platform-backend
github.com/armon/go-radix v1.0.0 // @grafana/grafana-app-platform-squad
github.com/aws/aws-sdk-go v1.55.7 // @grafana/aws-datasources
github.com/beevik/etree v1.4.1 // @grafana/grafana-backend-group
@ -94,14 +94,14 @@ require (
github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana-google-sdk-go v0.2.1 // @grafana/partner-datasources
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 // @grafana/grafana-backend-group
github.com/grafana/grafana-plugin-sdk-go v0.277.0 // @grafana/plugins-platform-backend
github.com/grafana/grafana-plugin-sdk-go v0.278.0 // @grafana/plugins-platform-backend
github.com/grafana/loki/v3 v3.2.1 // @grafana/observability-logs
github.com/grafana/nanogit v0.0.0-20250619160700-ebf70d342aa5 // @grafana-app-platform-squad
github.com/grafana/nanogit v0.0.0-20250625082556-a4828b879bbb // @grafana-app-platform-squad
github.com/grafana/otel-profiling-go v0.5.1 // @grafana/grafana-backend-group
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // @grafana/observability-traces-and-profiling
github.com/grafana/pyroscope/api v1.2.1-0.20250415190842-3ff7247547ae // @grafana/observability-traces-and-profiling
github.com/grafana/tempo v1.5.1-0.20250529124718-87c2dc380cec // @grafana/observability-traces-and-profiling
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // @grafana/plugins-platform-backend
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // @grafana/plugins-platform-backend
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // @grafana/grafana-backend-group
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // @grafana/identity-access-team
github.com/hashicorp/go-hclog v1.6.3 // @grafana/plugins-platform-backend
@ -164,8 +164,8 @@ require (
github.com/yudai/gojsondiff v1.0.0 // @grafana/grafana-backend-group
go.opentelemetry.io/collector/pdata v1.30.0 // @grafana/grafana-backend-group
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // @grafana/plugins-platform-backend
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // @grafana/grafana-operator-experience-squad
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // @grafana/grafana-backend-group
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 // @grafana/grafana-operator-experience-squad
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 // @grafana/grafana-backend-group
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 // @grafana/grafana-backend-group
go.opentelemetry.io/otel v1.36.0 // @grafana/grafana-backend-group
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // @grafana/grafana-backend-group
@ -324,7 +324,7 @@ require (
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
@ -541,7 +541,7 @@ require (
go.opencensus.io v0.24.0 // @grafana/grafana-backend-group
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // @grafana/sharing-squad
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // @grafana/sharing-squad
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
@ -575,7 +575,10 @@ require (
sigs.k8s.io/yaml v1.4.0 // indirect
)
require github.com/dgraph-io/badger/v4 v4.7.0 // @grafana/grafana-search-and-storage
require (
github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect
github.com/hashicorp/go-metrics v0.5.4 // indirect
github.com/jaegertracing/jaeger-idl v0.5.0 // indirect
github.com/sercand/kuberesolver/v6 v6.0.0 // indirect

@ -809,8 +809,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuW
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/apache/arrow-go/v18 v18.2.0 h1:QhWqpgZMKfWOniGPhbUxrHohWnooGURqL2R2Gg4SO1Q=
github.com/apache/arrow-go/v18 v18.2.0/go.mod h1:Ic/01WSwGJWRrdAZcxjBZ5hbApNJ28K96jGYaxzzGUc=
github.com/apache/arrow-go/v18 v18.3.0 h1:Xq4A6dZj9Nu33sqZibzn012LNnewkTUlfKVUFD/RX/I=
github.com/apache/arrow-go/v18 v18.3.0/go.mod h1:eEM1DnUTHhgGAjf/ChvOAQbUQ+EPohtDrArffvUjPg8=
github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0=
github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@ -1000,8 +1000,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 h1:VnjHsRXCRti7Av7E+j4DCha3kf68echfDzQ+wD11SBU=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 h1:UZdrvid2JFwnvPlUSEFlE794XZL4Jmrj8fuxfcLECJE=
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@ -1065,6 +1065,12 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE=
github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
github.com/dgraph-io/badger/v4 v4.7.0 h1:Q+J8HApYAY7UMpL8d9owqiB+odzEc0zn/aqOD9jhc6Y=
github.com/dgraph-io/badger/v4 v4.7.0/go.mod h1:He7TzG3YBy3j4f5baj5B7Zl2XyfNe5bl4Udl0aPemVA=
github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc h1:8WFBn63wegobsYAX0YjD+8suexZDga5CctH4CCTx2+8=
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
@ -1605,8 +1611,8 @@ github.com/grafana/grafana-google-sdk-go v0.2.1 h1:XeFdKnkXBjOJjXc1gf4iMx4h5aCHT
github.com/grafana/grafana-google-sdk-go v0.2.1/go.mod h1:RiITSHwBhqVTTd3se3HQq5Ncs/wzzhTB9OK5N0J0PEU=
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 h1:r+mU5bGMzcXCRVAuOrTn54S80qbfVkvTdUJZfSfTNbs=
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79/go.mod h1:wc6Hbh3K2TgCUSfBC/BOzabItujtHMESZeFk5ZhdxhQ=
github.com/grafana/grafana-plugin-sdk-go v0.277.0 h1:VDU2F4Y5NeRS//ejctdZtsAshrGaEdbtW33FsK0EQss=
github.com/grafana/grafana-plugin-sdk-go v0.277.0/go.mod h1:mAUWg68w5+1f5TLDqagIr8sWr1RT9h7ufJl5NMcWJAU=
github.com/grafana/grafana-plugin-sdk-go v0.278.0 h1:5/rIYparLi02pofdaag8wnjspMMVNCi8cZhC4cdC3Ho=
github.com/grafana/grafana-plugin-sdk-go v0.278.0/go.mod h1:+8NXT/XUJ/89GV6FxGQ366NZ3nU+cAXDMd0OUESF9H4=
github.com/grafana/grafana/apps/advisor v0.0.0-20250527064921-326081cdb7a1 h1:k7Gj+eevux9Z3u4t/oWiogCoFe5IpW13aCTGpKT862s=
github.com/grafana/grafana/apps/advisor v0.0.0-20250527064921-326081cdb7a1/go.mod h1:T4eaJioKjG8GQJS6sf8xMrzWV1QZrcOjxrSf4hbBiXM=
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250527064921-326081cdb7a1 h1:aE74WuajAAxB3FwMlIDjpsGoGLRsk4OOMvEDBLd+fmk=
@ -1637,8 +1643,8 @@ github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 h1:ZYk42718k
github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608/go.mod h1:f3JSoxBTPXX5ec4FxxeC19nTBSxoTz+cBgS3cYLMcr0=
github.com/grafana/loki/v3 v3.2.1 h1:VB7u+KHfvL5aHAxgoVBvz5wVhsdGuqKC7uuOFOOe7jw=
github.com/grafana/loki/v3 v3.2.1/go.mod h1:WvdLl6wOS+yahaeQY+xhD2m2XzkHDfKr5FZaX7D/X2Y=
github.com/grafana/nanogit v0.0.0-20250619160700-ebf70d342aa5 h1:MAQ2B0cu0V1S91ZjVa7NomNZFjaR2SmdtvdwhqBtyhU=
github.com/grafana/nanogit v0.0.0-20250619160700-ebf70d342aa5/go.mod h1:tN93IZUaAmnSWgL0IgnKdLv6DNeIhTJGvl1wvQMrWco=
github.com/grafana/nanogit v0.0.0-20250625082556-a4828b879bbb h1:JR672zBiCkfiKuRRsU76TRC2q12q0VfZl2SP8jBIAJQ=
github.com/grafana/nanogit v0.0.0-20250625082556-a4828b879bbb/go.mod h1:tN93IZUaAmnSWgL0IgnKdLv6DNeIhTJGvl1wvQMrWco=
github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8=
github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250604130045-92c8f6389b36 h1:AjZ58JRw1ZieFH/SdsddF5BXtsDKt5kSrKNPWrzYz3Y=
@ -1657,8 +1663,8 @@ github.com/grafana/tempo v1.5.1-0.20250529124718-87c2dc380cec h1:wnzJov9RhSHGaTY
github.com/grafana/tempo v1.5.1-0.20250529124718-87c2dc380cec/go.mod h1:j1IY7J2rUz7TcTjFVVx6HCpyTlYOJPtXuGRZ7sI+vSo=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
@ -2559,13 +2565,13 @@ go.opentelemetry.io/contrib/exporters/autoexport v0.61.0/go.mod h1:N6otC+qXTD5bA
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0/go.mod h1:rsg1EO8LXSs2po50PB5CeY/MSVlhghuKBgXlKnqm6ks=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 h1:lREC4C0ilyP4WibDhQ7Gg2ygAQFP8oR07Fst/5cafwI=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0/go.mod h1:HfvuU0kW9HewH14VCOLImqKvUgONodURG7Alj/IrnGI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 h1:SoCgXYF4ISDtNyfLUzsGDaaudZVTx2yJhOyBO0+/GYk=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0/go.mod h1:VHu48l0YTRKSObdPQ+Sb8xMZvdnJlN7yhHuHoPgNqHM=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 h1:bQ1Gvah4Sp8z7epSkgJaNTuZm7sutfA6Fji2/7cKFMc=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0/go.mod h1:9b8Q9rH52NgYH3ShiTFB5wf18Vt3RTH/VMB7LDcC1ug=
go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0=

@ -867,6 +867,8 @@ github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgf
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chromedp/sysutil v1.1.0 h1:PUFNv5EcprjqXZD9nJb9b/c9ibAbxiYo4exNWZyipwM=
github.com/chromedp/sysutil v1.1.0/go.mod h1:WiThHUdltqCNKGc4gaU50XgYjwjYIhKWoHGPTUfWTJ8=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
@ -1150,6 +1152,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535 h1:yE7argOs92u+sSCRgqqe6eF+cDaVhSPlioy1UkA0p/w=
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535/go.mod h1:BWmvoE1Xia34f3l/ibJweyhrT+aROb/FQ6d+37F0e2s=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4=
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
@ -1240,6 +1244,7 @@ github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwg
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
@ -1335,6 +1340,7 @@ github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJr
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.2.0/go.mod h1:zrT2dxOAjNFPRGjTUe2Xmb4q4YdUwVvQFV6xiCSf+z0=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
@ -1916,6 +1922,8 @@ github.com/substrait-io/substrait-go v1.2.0 h1:3ZNRkc8FYD7ifCagKEOZQtUcgMceMQfwo
github.com/substrait-io/substrait-go v1.2.0/go.mod h1:IPsy24rdjp/buXR+T8ENl6QCnSCS6h+uM8P+GaZez7c=
github.com/substrait-io/substrait-go/v3 v3.9.0 h1:sRJf0ID9q2TPxJ9eH+oAniepMqt9fYW0Hy32CScT2cI=
github.com/substrait-io/substrait-go/v3 v3.9.0/go.mod h1:VG7jCqtUm28bSngHwq86FywtU74knJ25LNX63SZ53+E=
github.com/substrait-io/substrait-go/v3 v3.9.1 h1:2yfHDHpK6KMcvLd0bJVzUJoeXO+K98yS+ciBruxD9po=
github.com/substrait-io/substrait-go/v3 v3.9.1/go.mod h1:VG7jCqtUm28bSngHwq86FywtU74knJ25LNX63SZ53+E=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/tchap/go-patricia v2.2.6+incompatible h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPxmzYe32HHy5yQ+Ck=
github.com/tdewolff/minify/v2 v2.12.8 h1:Q2BqOTmlMjoutkuD/OPCnJUpIqrzT3nRPkw+q+KpXS0=
@ -2293,6 +2301,8 @@ go.opentelemetry.io/contrib/propagators/b3 v1.35.0 h1:DpwKW04LkdFRFCIgM3sqwTJA/Q
go.opentelemetry.io/contrib/propagators/b3 v1.35.0/go.mod h1:9+SNxwqvCWo1qQwUpACBY5YKNVxFJn5mlbXg/4+uKBg=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0/go.mod h1:iWS+NvC948FyfnJbVfPN9h/8+vr8CR2FPn6XsLRkvH8=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.29.0/go.mod h1:XAJmM2MWhiIoTO4LCLBVeE8w009TmsYk6hq1UNdXs5A=
go.opentelemetry.io/contrib/zpages v0.60.0 h1:wOM9ie1Hz4H88L9KE6GrGbKJhfm+8F1NfW/Y3q9Xt+8=
go.opentelemetry.io/contrib/zpages v0.60.0/go.mod h1:xqfToSRGh2MYUsfyErNz8jnNDPlnpZqWM/y6Z2Cx7xw=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4=
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
@ -2402,7 +2412,10 @@ golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCR
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM=
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
@ -2708,6 +2721,7 @@ modernc.org/ccgo/v3 v3.17.0/go.mod h1:Sg3fwVpmLvCUTaqEUjiBDAvshIaKDB0RXaf+zgqFu8
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/ccorpus2 v1.5.2 h1:Ui+4tc58mf/W+2arcYCJR903y3zl3ecsI7Fpaaqozyw=
modernc.org/ccorpus2 v1.5.2/go.mod h1:Wifvo4Q/qS/h1aRoC2TffcHsnxwTikmi1AuLANuucJQ=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
@ -2716,8 +2730,10 @@ modernc.org/lex v1.1.1/go.mod h1:6r8o8DLJkAnOsQaGi8fMoi+Vt6LTbDaCrkUK729D8xM=
modernc.org/lexer v1.0.4 h1:hU7xVbZsqwPphyzChc7nMSGrsuaD2PDNOmzrzkS5AlE=
modernc.org/lexer v1.0.4/go.mod h1:tOajb8S4sdfOYitzCgXDFmbVJ/LE0v1fNJ7annTw36U=
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/scannertest v1.0.2 h1:JPtfxcVdbRvzmRf2YUvsDibJsQRw8vKA/3jb31y7cy0=
modernc.org/scannertest v1.0.2/go.mod h1:RzTm5RwglF/6shsKoEivo8N91nQIoWtcWI7ns+zPyGA=
modernc.org/sqlite v1.29.6/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=

@ -259,7 +259,8 @@
"webpack-subresource-integrity": "^5.2.0-rc.1",
"webpackbar": "^7.0.0",
"yaml": "^2.0.0",
"yargs": "^17.5.1"
"yargs": "^17.5.1",
"zod": "^3.25.55"
},
"dependencies": {
"@bsull/augurs": "^0.10.0",
@ -287,12 +288,12 @@
"@grafana/plugin-ui": "0.10.7",
"@grafana/prometheus": "workspace:*",
"@grafana/runtime": "workspace:*",
"@grafana/scenes": "^6.21.0",
"@grafana/scenes-react": "^6.21.0",
"@grafana/scenes": "^6.22.1",
"@grafana/scenes-react": "^6.22.1",
"@grafana/schema": "workspace:*",
"@grafana/sql": "workspace:*",
"@grafana/ui": "workspace:*",
"@hello-pangea/dnd": "17.0.0",
"@hello-pangea/dnd": "18.0.1",
"@kusto/monaco-kusto": "^10.0.0",
"@leeoniya/ufuzzy": "1.0.18",
"@lezer/common": "1.2.3",
@ -454,7 +455,7 @@
"engines": {
"node": ">= 22 <23"
},
"packageManager": "yarn@4.6.0",
"packageManager": "yarn@4.9.2",
"dependenciesMeta": {
"prettier@3.4.2": {
"unplugged": true

@ -1032,4 +1032,9 @@ export interface FeatureToggles {
* @default false
*/
preferLibraryPanelTitle?: boolean;
/**
* Use fixed-width numbers globally in the UI
* @default true
*/
tabularNumbers?: boolean;
}

@ -22,5 +22,5 @@
"webpack": "5.97.1",
"webpack-bundle-analyzer": "^4.10.2"
},
"packageManager": "yarn@4.6.0"
"packageManager": "yarn@4.9.2"
}

@ -48,7 +48,7 @@
"@grafana/runtime": "12.1.0-pre",
"@grafana/schema": "12.1.0-pre",
"@grafana/ui": "12.1.0-pre",
"@hello-pangea/dnd": "17.0.0",
"@hello-pangea/dnd": "18.0.1",
"@leeoniya/ufuzzy": "1.0.18",
"@lezer/common": "1.2.3",
"@lezer/highlight": "1.2.1",

@ -825,6 +825,21 @@ describe('PrometheusLanguageProvider with feature toggle', () => {
expect(result).toEqual(['label1', 'label2']);
});
it('queryLabelKeys should interpolate variables', async () => {
const provider = new PrometheusLanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => string.replace(/\$/g, 'interpolated_'),
} as PrometheusDatasource);
const resourceClientSpy = jest
.spyOn(provider['resourceClient'], 'queryLabelKeys')
.mockResolvedValue(['label1', 'label2']);
const result = await provider.queryLabelKeys(timeRange, '{job="$job"}');
expect(resourceClientSpy).toHaveBeenCalledWith(timeRange, '{job="interpolated_job"}', undefined);
expect(result).toEqual(['label1', 'label2']);
});
it('should delegate to resource client queryLabelValues', async () => {
const provider = new PrometheusLanguageProvider(defaultDatasource);
const resourceClientSpy = jest
@ -836,6 +851,26 @@ describe('PrometheusLanguageProvider with feature toggle', () => {
expect(resourceClientSpy).toHaveBeenCalledWith(timeRange, 'job', '{job="grafana"}', undefined);
expect(result).toEqual(['value1', 'value2']);
});
it('queryLabelValues should interpolate variables', async () => {
const provider = new PrometheusLanguageProvider({
...defaultDatasource,
interpolateString: (string: string) => string.replace(/\$/g, 'interpolated_'),
} as PrometheusDatasource);
const resourceClientSpy = jest
.spyOn(provider['resourceClient'], 'queryLabelValues')
.mockResolvedValue(['label1', 'label2']);
const result = await provider.queryLabelValues(timeRange, '$label', '{job="$job"}');
expect(resourceClientSpy).toHaveBeenCalledWith(
timeRange,
'interpolated_label',
'{job="interpolated_job"}',
undefined
);
expect(result).toEqual(['label1', 'label2']);
});
});
describe('retrieveMethods', () => {

@ -682,7 +682,8 @@ export class PrometheusLanguageProvider extends PromQlLanguageProvider implement
* @returns {Promise<string[]>} Array of matching label key names, sorted alphabetically
*/
public queryLabelKeys = async (timeRange: TimeRange, match?: string, limit?: number): Promise<string[]> => {
return await this.resourceClient.queryLabelKeys(timeRange, match, limit);
const interpolatedMatch = match ? this.datasource.interpolateString(match) : match;
return await this.resourceClient.queryLabelKeys(timeRange, interpolatedMatch, limit);
};
/**
@ -716,7 +717,13 @@ export class PrometheusLanguageProvider extends PromQlLanguageProvider implement
match?: string,
limit?: number
): Promise<string[]> => {
return await this.resourceClient.queryLabelValues(timeRange, labelKey, match, limit);
const interpolatedMatch = match ? this.datasource.interpolateString(match) : match;
return await this.resourceClient.queryLabelValues(
timeRange,
this.datasource.interpolateString(labelKey),
interpolatedMatch,
limit
);
};
}

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Výstrahy",
"tooltip-manage-alerts-via-alerting-ui": "Spravovat pravidla výstrah pro tento zdroj dat. Chcete-li spravovat další zdroje Alerting, přidejte zdroj dat Alertmanager.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Spravovat pravidla výstrah pro tento zdroj dat. Chcete-li spravovat další zdroje Alerting, přidejte zdroj dat Alertmanager."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Meldungen",
"tooltip-manage-alerts-via-alerting-ui": "Verwalten Sie Warnregeln für diese Datenquelle. Fügen Sie eine Altermanager-Datenquelle hinzu, um andere Warnressourcen zu verwalten.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Verwalten Sie Warnregeln für diese Datenquelle. Fügen Sie eine Altermanager-Datenquelle hinzu, um andere Warnressourcen zu verwalten."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -170,8 +170,8 @@
"label-query-overlap-window": "Query overlap window",
"label-query-timeout": "Query timeout",
"label-scrape-interval": "Scrape interval",
"label-use-series-endpoint": "Use series endpoint",
"label-series-limit": "Series limit",
"label-use-series-endpoint": "Use series endpoint",
"more-info": "For more information on configuring prometheus type and version in data sources, see the <2>provisioning documentation</2>.",
"placeholder-example-maxsourceresolutionmtimeout": "Example: {{example}}",
"title-interval-behaviour": "Interval behaviour",
@ -191,8 +191,8 @@
"tooltip-query-overlap-window": "Set a duration like {{example1}} or {{example2}} or {{example3}}. Default of {{default}}. This duration will be added to the duration of each incremental request.",
"tooltip-query-timeout": "Set the Prometheus query timeout.",
"tooltip-scrape-interval": "This interval is how frequently Prometheus scrapes targets. Set this to the typical scrape and evaluation interval configured in your Prometheus config file. If you set this to a greater value than your Prometheus config file interval, Grafana will evaluate the data according to this interval and you will see less data points. Defaults to {{default}}.",
"tooltip-use-series-endpoint": "Checking this option will favor the series endpoint with {{exampleParameter}} parameter over the label values endpoint with {{exampleParameter}} parameter. While the label values endpoint is considered more performant, some users may prefer the series because it has a POST method while the label values endpoint only has a GET method.",
"tooltip-series-limit": "The limit applies to all resources (metrics, labels, and values) for both endpoints (series and labels). Leave the field empty to use the default limit (40000). Set to 0 to disable the limit and fetch everything — this may cause performance issues. Default limit is 40000."
"tooltip-series-limit": "The limit applies to all resources (metrics, labels, and values) for both endpoints (series and labels). Leave the field empty to use the default limit (40000). Set to 0 to disable the limit and fetch everything — this may cause performance issues. Default limit is 40000.",
"tooltip-use-series-endpoint": "Checking this option will favor the series endpoint with {{exampleParameter}} parameter over the label values endpoint with {{exampleParameter}} parameter. While the label values endpoint is considered more performant, some users may prefer the series because it has a POST method while the label values endpoint only has a GET method."
}
},
"querybuilder": {

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Alertas",
"tooltip-manage-alerts-via-alerting-ui": "Gestiona las reglas de alerta para esta fuente de datos. Para gestionar otros recursos de alerta, añade una fuente de datos de Alertmanager.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Gestiona las reglas de alerta para esta fuente de datos. Para gestionar otros recursos de alerta, añade una fuente de datos de Alertmanager."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Alerte",
"tooltip-manage-alerts-via-alerting-ui": "Gérez les règles d'alerte pour cette source de données. Pour gérer d'autres ressources d'alerte, ajoutez une source de données Alertmanager.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Gérez les règles d'alerte pour cette source de données. Pour gérer d'autres ressources d'alerte, ajoutez une source de données Alertmanager."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Riasztás",
"tooltip-manage-alerts-via-alerting-ui": "Riasztási szabályok kezelése ehhez az adatforráshoz. Más Alerting-erőforrások kezeléséhez adjon hozzá egy Alertmanager-adatforrást.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Riasztási szabályok kezelése ehhez az adatforráshoz. Más Alerting-erőforrások kezeléséhez adjon hozzá egy Alertmanager-adatforrást."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Alerting",
"tooltip-manage-alerts-via-alerting-ui": "Kelola aturan peringatan untuk sumber data ini. Untuk mengelola sumber daya alerting lainnya, tambahkan sumber data Alertmanager.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Kelola aturan peringatan untuk sumber data ini. Untuk mengelola sumber daya alerting lainnya, tambahkan sumber data Alertmanager."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Avvisi",
"tooltip-manage-alerts-via-alerting-ui": "Gestisci le regole di avviso per questa origine dati. Per gestire altre risorse di avviso, aggiungi un'origine dati Alertmanager.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Gestisci le regole di avviso per questa origine dati. Per gestire altre risorse di avviso, aggiungi un'origine dati Alertmanager."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "アラート",
"tooltip-manage-alerts-via-alerting-ui": "このデータソースのアラートルールを管理します。他のアラートリソースを管理するには、Alertmanagerデータソースを追加します。",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "このデータソースのアラートルールを管理します。他のアラートリソースを管理するには、Alertmanagerデータソースを追加します。"
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "경고",
"tooltip-manage-alerts-via-alerting-ui": "이 데이터 소스에 대한 경고 규칙을 관리합니다. 다른 Alerting 리소스를 관리하려면 Alertmanager 데이터 소스를 추가하세요.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "이 데이터 소스에 대한 경고 규칙을 관리합니다. 다른 Alerting 리소스를 관리하려면 Alertmanager 데이터 소스를 추가하세요."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Alerting",
"tooltip-manage-alerts-via-alerting-ui": "Waarschuwingsregels voor deze gegevensbron beheren. Om andere waarschuwingsbronnen te beheren, voeg je een Alertmanager-gegevensbron toe.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Waarschuwingsregels voor deze gegevensbron beheren. Om andere waarschuwingsbronnen te beheren, voeg je een Alertmanager-gegevensbron toe."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Alerty",
"tooltip-manage-alerts-via-alerting-ui": "Zarządzaj regułami alertów dla tego źródła danych. Aby zarządzać innymi zasobami alertów, dodaj źródło danych Alertmanager.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Zarządzaj regułami alertów dla tego źródła danych. Aby zarządzać innymi zasobami alertów, dodaj źródło danych Alertmanager."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Alertas",
"tooltip-manage-alerts-via-alerting-ui": "Gerenciar regras de alerta para esta fonte de dados. Para gerenciar outros recursos do alerting, adicione uma fonte de dados do Alertmanager.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Gerenciar regras de alerta para esta fonte de dados. Para gerenciar outros recursos do alerting, adicione uma fonte de dados do Alertmanager."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Alerta",
"tooltip-manage-alerts-via-alerting-ui": "Gerir regras de alerta para esta origem de dados. Para gerir outros recursos do Alerting, adicione uma origem de dados do Alertmanager.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Gerir regras de alerta para esta origem de dados. Para gerir outros recursos do Alerting, adicione uma origem de dados do Alertmanager."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Оповещение",
"tooltip-manage-alerts-via-alerting-ui": "Управление правилами оповещения для этого источника данных. Чтобы управлять другими ресурсами Alerting, добавьте обработчик оповещений Alertmanager в качестве источника данных.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Управление правилами оповещения для этого источника данных. Чтобы управлять другими ресурсами Alerting, добавьте обработчик оповещений Alertmanager в качестве источника данных."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Varnar",
"tooltip-manage-alerts-via-alerting-ui": "Hantera varningsregler för den här datakällan. Om du vill hantera andra varningsresurser ska du lägga till en Alertmanager-datakälla.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Hantera varningsregler för den här datakällan. Om du vill hantera andra varningsresurser ska du lägga till en Alertmanager-datakälla."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "Uyarı sistemi",
"tooltip-manage-alerts-via-alerting-ui": "Bu veri kaynağı için uyarı kurallarını yönetin. Diğer Alerting kaynaklarını yönetmek için bir Alertmanager veri kaynağı ekleyin.",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "Bu veri kaynağı için uyarı kurallarını yönetin. Diğer Alerting kaynaklarını yönetmek için bir Alertmanager veri kaynağı ekleyin."
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "警报",
"tooltip-manage-alerts-via-alerting-ui": "管理此数据源的提醒规则。要管理其他 Alerting 资源,请添加 Alertmanager 数据源。",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "管理此数据源的提醒规则。要管理其他 Alerting 资源,请添加 Alertmanager 数据源。"
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -110,11 +110,11 @@
},
"configuration": {
"alerting-settings-overhaul": {
"label-manage-alerts-via-alerting-ui": "",
"label-allow-as-recording-rules-target": "",
"label-manage-alerts-via-alerting-ui": "",
"title-alerting": "警報",
"tooltip-manage-alerts-via-alerting-ui": "管理此資料來源的警報規則。若要管理其他警報資源,請新增 Alertmanager 資料來源。",
"tooltip-allow-as-recording-rules-target": ""
"tooltip-allow-as-recording-rules-target": "",
"tooltip-manage-alerts-via-alerting-ui": "管理此資料來源的警報規則。若要管理其他警報資源,請新增 Alertmanager 資料來源。"
},
"config-editor": {
"browser-access-mode-error": "",
@ -170,6 +170,7 @@
"label-query-overlap-window": "",
"label-query-timeout": "",
"label-scrape-interval": "",
"label-series-limit": "",
"label-use-series-endpoint": "",
"more-info": "",
"placeholder-example-maxsourceresolutionmtimeout": "",
@ -190,6 +191,7 @@
"tooltip-query-overlap-window": "",
"tooltip-query-timeout": "",
"tooltip-scrape-interval": "",
"tooltip-series-limit": "",
"tooltip-use-series-endpoint": ""
}
},

@ -862,6 +862,7 @@ export enum VariableFormatID {
Glob = 'glob',
HTML = 'html',
JSON = 'json',
Join = 'join',
Lucene = 'lucene',
PercentEncode = 'percentencode',
Pipe = 'pipe',

@ -3,6 +3,8 @@ package common
// Optional formats for the template variable replace functions
// See also https://grafana.com/docs/grafana/latest/dashboards/variables/variable-syntax/#advanced-variable-format-options
VariableFormatID:
// Values are joined with a separator
"join" |
// Values are lucene escaped and multi-valued variables generate an OR expression
"lucene" |
// Raw values
@ -36,4 +38,4 @@ VariableFormatID:
// Format variables in their text representation. Example in multi-variable scenario A + B + C.
"text" |
// Format variables as URL parameters. Example in multi-variable scenario A + B + C => var-foo=A&var-foo=B&var-foo=C.
"queryparam" @cuetsy(kind="enum",memberNames="Lucene|Raw|Regex|Pipe|Distributed|CSV|HTML|JSON|PercentEncode|UriEncode|SingleQuote|DoubleQuote|SQLString|Date|Glob|Text|QueryParam")
"queryparam" @cuetsy(kind="enum",memberNames="Join|Lucene|Raw|Regex|Pipe|Distributed|CSV|HTML|JSON|PercentEncode|UriEncode|SingleQuote|DoubleQuote|SQLString|Date|Glob|Text|QueryParam")

@ -71,7 +71,7 @@
"@grafana/faro-web-sdk": "^1.13.2",
"@grafana/i18n": "12.1.0-pre",
"@grafana/schema": "12.1.0-pre",
"@hello-pangea/dnd": "17.0.0",
"@hello-pangea/dnd": "18.0.1",
"@leeoniya/ufuzzy": "1.0.18",
"@monaco-editor/react": "4.7.0",
"@popperjs/core": "2.11.8",

@ -28,11 +28,14 @@ export const FieldNameByRegexMatcherEditor = memo<MatcherUIProps<string>>((props
});
FieldNameByRegexMatcherEditor.displayName = 'FieldNameByRegexMatcherEditor';
export const fieldNameByRegexMatcherItem: FieldMatcherUIRegistryItem<string> = {
export const getFieldNameByRegexMatcherItem: () => FieldMatcherUIRegistryItem<string> = () => ({
id: FieldMatcherID.byRegexp,
component: FieldNameByRegexMatcherEditor,
matcher: fieldMatchers.get(FieldMatcherID.byRegexp),
name: 'Fields with name matching regex',
description: 'Set properties for fields with names matching a regex',
name: t('grafana-ui.matchers-ui.name-field-name-by-regex-matcher', 'Fields with name matching regex'),
description: t(
'grafana-ui.matchers-ui.description-field-name-by-regex-matcher',
'Set properties for fields with names matching a regex'
),
optionsToLabel: (options) => options,
};
});

@ -1,6 +1,7 @@
import { memo, useCallback } from 'react';
import { FieldMatcherID, fieldMatchers, SelectableValue } from '@grafana/data';
import { t } from '@grafana/i18n';
import { Select } from '../Select/Select';
@ -27,11 +28,11 @@ export const FieldNameMatcherEditor = memo<MatcherUIProps<string>>((props) => {
});
FieldNameMatcherEditor.displayName = 'FieldNameMatcherEditor';
export const fieldNameMatcherItem: FieldMatcherUIRegistryItem<string> = {
export const getFieldNameMatcherItem: () => FieldMatcherUIRegistryItem<string> = () => ({
id: FieldMatcherID.byName,
component: FieldNameMatcherEditor,
matcher: fieldMatchers.get(FieldMatcherID.byName),
name: 'Fields with name',
description: 'Set properties for a specific field',
name: t('grafana-ui.matchers-ui.name-fields-with-name', 'Fields with name'),
description: t('grafana-ui.matchers-ui.description-fields-with-name', 'Set properties for a specific field'),
optionsToLabel: (options) => options,
};
});

@ -1,6 +1,7 @@
import { useCallback } from 'react';
import { FieldNamePickerConfigSettings, SelectableValue, StandardEditorProps } from '@grafana/data';
import { t } from '@grafana/i18n';
import { Select } from '../Select/Select';
@ -29,7 +30,9 @@ export const FieldNamePicker = ({ value, onChange, context, item }: Props) => {
<>
<Select
value={selectedOption}
placeholder={settings.placeholderText ?? 'Select field'}
placeholder={
settings.placeholderText ?? t('grafana-ui.matchers-ui.field-name-picker.placeholder', 'Select field')
}
options={selectOptions}
onChange={onSelectChange}
noOptionsMessage={settings.noFieldsMessage}

@ -1,6 +1,7 @@
import { memo, useCallback } from 'react';
import { FieldMatcherID, fieldMatchers, SelectableValue, ByNamesMatcherOptions } from '@grafana/data';
import { t } from '@grafana/i18n';
import { Input } from '../Input/Input';
import { MultiSelect } from '../Select/Select';
@ -43,12 +44,12 @@ export const FieldNamesMatcherEditor = memo<MatcherUIProps<ByNamesMatcherOptions
});
FieldNamesMatcherEditor.displayName = 'FieldNameMatcherEditor';
export const fieldNamesMatcherItem: FieldMatcherUIRegistryItem<ByNamesMatcherOptions> = {
export const getFieldNamesMatcherItem: () => FieldMatcherUIRegistryItem<ByNamesMatcherOptions> = () => ({
id: FieldMatcherID.byNames,
component: FieldNamesMatcherEditor,
matcher: fieldMatchers.get(FieldMatcherID.byNames),
name: 'Fields with name',
description: 'Set properties for a specific field',
name: t('grafana-ui.matchers-ui.name-fields-with-name', 'Fields with name'),
description: t('grafana-ui.matchers-ui.description-fields-with-name', 'Set properties for a specific field'),
optionsToLabel: (options) => (options.names ?? []).join(', '),
excludeFromPicker: true,
};
});

@ -1,6 +1,7 @@
import { memo, useMemo, useCallback } from 'react';
import { FieldMatcherID, fieldMatchers, SelectableValue, FieldType, DataFrame } from '@grafana/data';
import { t } from '@grafana/i18n';
import { getFieldTypeIconName } from '../../types/icon';
import { Select } from '../Select/Select';
@ -83,11 +84,14 @@ const useSelectOptions = (counts: Map<string, number>, opt?: string): Array<Sele
}, [counts, opt]);
};
export const fieldTypeMatcherItem: FieldMatcherUIRegistryItem<string> = {
export const getFieldTypeMatcherItem: () => FieldMatcherUIRegistryItem<string> = () => ({
id: FieldMatcherID.byType,
component: FieldTypeMatcherEditor,
matcher: fieldMatchers.get(FieldMatcherID.byType),
name: 'Fields with type',
description: 'Set properties for fields of a specific type (number, string, boolean)',
name: t('grafana-ui.matchers-ui.name-fields-with-type', 'Fields with type'),
description: t(
'grafana-ui.matchers-ui.description-fields-with-type',
'Set properties for fields of a specific type (number, string, boolean)'
),
optionsToLabel: (options) => options,
};
});

@ -102,11 +102,14 @@ const getStyles = (theme: GrafanaTheme2) => {
};
};
export const fieldValueMatcherItem: FieldMatcherUIRegistryItem<FieldValueMatcherConfig> = {
export const getFieldValueMatcherItem: () => FieldMatcherUIRegistryItem<FieldValueMatcherConfig> = () => ({
id: FieldMatcherID.byValue,
component: FieldValueMatcherEditor,
matcher: fieldMatchers.get(FieldMatcherID.byValue),
name: 'Fields with values',
description: 'Set properties for fields with reducer condition',
name: t('grafana-ui.matchers-ui.name-fields-with-value', 'Fields with values'),
description: t(
'grafana-ui.matchers-ui.description-fields-with-value',
'Set properties for fields with reducer condition'
),
optionsToLabel: (options) => `${options?.reducer} ${options?.op} ${options?.value}`,
};
});

@ -8,6 +8,7 @@ import {
SelectableValue,
toOption,
} from '@grafana/data';
import { t } from '@grafana/i18n';
import { MultiSelect, Select } from '../Select/Select';
@ -216,18 +217,20 @@ function getFramesDescription(frames: DataFrame[]): string {
/**
* Registry item for UI to configure "fields by frame refId"-matcher.
* @public
*/
export const fieldsByFrameRefIdItem: FieldMatcherUIRegistryItem<string> = {
export const getFieldsByFrameRefIdItem: () => FieldMatcherUIRegistryItem<string> = () => ({
id: FieldMatcherID.byFrameRefID,
component: (props: MatcherUIProps<string>) => {
return <RefIDPicker value={props.options} data={props.data} onChange={props.onChange} />;
},
matcher: fieldMatchers.get(FieldMatcherID.byFrameRefID),
name: 'Fields returned by query',
description: 'Set properties for fields from a specific query',
name: t('grafana-ui.matchers-ui.name-fields-by-query', 'Fields returned by query'),
description: t(
'grafana-ui.matchers-ui.description-fields-by-query',
'Set properties for fields from a specific query'
),
optionsToLabel: (options) => options,
};
});
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#escaping
function escapeRegExp(string: string) {

@ -1,18 +1,18 @@
import { Registry } from '@grafana/data';
import { fieldNameByRegexMatcherItem } from './FieldNameByRegexMatcherEditor';
import { fieldNameMatcherItem } from './FieldNameMatcherEditor';
import { fieldNamesMatcherItem } from './FieldNamesMatcherEditor';
import { fieldTypeMatcherItem } from './FieldTypeMatcherEditor';
import { fieldValueMatcherItem } from './FieldValueMatcher';
import { fieldsByFrameRefIdItem } from './FieldsByFrameRefIdMatcher';
import { getFieldNameByRegexMatcherItem } from './FieldNameByRegexMatcherEditor';
import { getFieldNameMatcherItem } from './FieldNameMatcherEditor';
import { getFieldNamesMatcherItem } from './FieldNamesMatcherEditor';
import { getFieldTypeMatcherItem } from './FieldTypeMatcherEditor';
import { getFieldValueMatcherItem } from './FieldValueMatcher';
import { getFieldsByFrameRefIdItem } from './FieldsByFrameRefIdMatcher';
import { FieldMatcherUIRegistryItem } from './types';
export const fieldMatchersUI = new Registry<FieldMatcherUIRegistryItem<any>>(() => [
fieldNameMatcherItem,
fieldNameByRegexMatcherItem,
fieldTypeMatcherItem,
fieldsByFrameRefIdItem,
fieldNamesMatcherItem,
fieldValueMatcherItem,
getFieldNameMatcherItem(),
getFieldNameByRegexMatcherItem(),
getFieldTypeMatcherItem(),
getFieldsByFrameRefIdItem(),
getFieldNamesMatcherItem(),
getFieldValueMatcherItem(),
]);

@ -56,9 +56,11 @@ export const VizTooltipColorIndicator = ({
const getStyles = (theme: GrafanaTheme2) => ({
leading: css({
marginRight: theme.spacing(0.5),
flex: 'none',
}),
trailing: css({
marginLeft: theme.spacing(0.5),
flex: 'none',
}),
value: css({
width: '12px',

@ -52,8 +52,9 @@ export const VizTooltipRow = ({
overflowY: 'auto',
}
: {
whiteSpace: 'wrap',
whiteSpace: 'pre-line',
wordBreak: 'break-word',
lineHeight: 1.2,
};
const [showLabelTooltip, setShowLabelTooltip] = useState(false);
@ -129,7 +130,7 @@ export const VizTooltipRow = ({
return (
<div className={styles.contentWrapper}>
{(color || label) && (
<div className={styles.valueWrapper}>
<div className={styles.labelWrapper}>
{color && colorPlacement === ColorPlacement.first && (
<VizTooltipColorIndicator color={color} colorIndicator={colorIndicator} lineStyle={lineStyle} />
)}
@ -221,6 +222,12 @@ const getStyles = (theme: GrafanaTheme2, justify: string, marginRight: string) =
overflow: 'hidden',
marginRight: theme.spacing(2),
}),
labelWrapper: css({
display: 'flex',
alignItems: 'center',
flex: '2',
minWidth: 0,
}),
value: css({
fontWeight: 500,
textOverflow: 'ellipsis',
@ -229,6 +236,7 @@ const getStyles = (theme: GrafanaTheme2, justify: string, marginRight: string) =
valueWrapper: css({
display: 'flex',
alignItems: 'center',
flex: '1',
}),
activeSeries: css({
fontWeight: theme.typography.fontWeightBold,

@ -1,4 +1,5 @@
import { SelectableValue } from '@grafana/data';
import { t } from '@grafana/i18n';
import {
AxisPlacement,
BarAlignment,
@ -10,10 +11,7 @@ import {
StackingMode,
} from '@grafana/schema';
/**
* @alpha
*/
export const graphFieldOptions: {
export const getGraphFieldOptions: () => {
drawStyle: Array<SelectableValue<GraphDrawStyle>>;
lineInterpolation: Array<SelectableValue<LineInterpolation>>;
barAlignment: Array<SelectableValue<BarAlignment>>;
@ -22,62 +20,204 @@ export const graphFieldOptions: {
fillGradient: Array<SelectableValue<GraphGradientMode>>;
stacking: Array<SelectableValue<StackingMode>>;
thresholdsDisplayModes: Array<SelectableValue<GraphThresholdsStyleMode>>;
} = {
} = () => ({
drawStyle: [
{ label: 'Lines', value: GraphDrawStyle.Line },
{ label: 'Bars', value: GraphDrawStyle.Bars },
{ label: 'Points', value: GraphDrawStyle.Points },
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.draw-style.label-lines', 'Lines'),
value: GraphDrawStyle.Line,
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.draw-style.label-bars', 'Bars'),
value: GraphDrawStyle.Bars,
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.draw-style.label-points', 'Points'),
value: GraphDrawStyle.Points,
},
],
lineInterpolation: [
{ description: 'Linear', value: LineInterpolation.Linear, icon: 'gf-interpolation-linear' },
{ description: 'Smooth', value: LineInterpolation.Smooth, icon: 'gf-interpolation-smooth' },
{ description: 'Step before', value: LineInterpolation.StepBefore, icon: 'gf-interpolation-step-before' },
{ description: 'Step after', value: LineInterpolation.StepAfter, icon: 'gf-interpolation-step-after' },
{
description: t(
'grafana-ui.u-plot.config.get-graph-field-options.line-interpolation.description-linear',
'Linear'
),
value: LineInterpolation.Linear,
icon: 'gf-interpolation-linear',
},
{
description: t(
'grafana-ui.u-plot.config.get-graph-field-options.line-interpolation.description-smooth',
'Smooth'
),
value: LineInterpolation.Smooth,
icon: 'gf-interpolation-smooth',
},
{
description: t(
'grafana-ui.u-plot.config.get-graph-field-options.line-interpolation.description-step-before',
'Step before'
),
value: LineInterpolation.StepBefore,
icon: 'gf-interpolation-step-before',
},
{
description: t(
'grafana-ui.u-plot.config.get-graph-field-options.line-interpolation.description-step-after',
'Step after'
),
value: LineInterpolation.StepAfter,
icon: 'gf-interpolation-step-after',
},
],
barAlignment: [
{ description: 'Before', value: BarAlignment.Before, icon: 'gf-bar-alignment-before' },
{ description: 'Center', value: BarAlignment.Center, icon: 'gf-bar-alignment-center' },
{ description: 'After', value: BarAlignment.After, icon: 'gf-bar-alignment-after' },
{
description: t('grafana-ui.u-plot.config.get-graph-field-options.bar-alignment.description-before', 'Before'),
value: BarAlignment.Before,
icon: 'gf-bar-alignment-before',
},
{
description: t('grafana-ui.u-plot.config.get-graph-field-options.bar-alignment.description-center', 'Center'),
value: BarAlignment.Center,
icon: 'gf-bar-alignment-center',
},
{
description: t('grafana-ui.u-plot.config.get-graph-field-options.bar-alignment.description-after', 'After'),
value: BarAlignment.After,
icon: 'gf-bar-alignment-after',
},
],
showPoints: [
{ label: 'Auto', value: VisibilityMode.Auto, description: 'Show points when the density is low' },
{ label: 'Always', value: VisibilityMode.Always },
{ label: 'Never', value: VisibilityMode.Never },
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.show-points.label-auto', 'Auto'),
value: VisibilityMode.Auto,
description: t(
'grafana-ui.u-plot.config.get-graph-field-options.show-points.description-auto',
'Show points when the density is low'
),
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.show-points.label-always', 'Always'),
value: VisibilityMode.Always,
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.show-points.label-never', 'Never'),
value: VisibilityMode.Never,
},
],
axisPlacement: [
{ label: 'Auto', value: AxisPlacement.Auto, description: 'First field on the left, everything else on the right' },
{ label: 'Left', value: AxisPlacement.Left },
{ label: 'Right', value: AxisPlacement.Right },
{ label: 'Hidden', value: AxisPlacement.Hidden },
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.axis-placement.label-auto', 'Auto'),
value: AxisPlacement.Auto,
description: t(
'grafana-ui.u-plot.config.get-graph-field-options.axis-placement.description-auto',
'First field on the left, everything else on the right'
),
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.axis-placement.label-left', 'Left'),
value: AxisPlacement.Left,
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.axis-placement.label-right', 'Right'),
value: AxisPlacement.Right,
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.axis-placement.label-hidden', 'Hidden'),
value: AxisPlacement.Hidden,
},
],
fillGradient: [
{ label: 'None', value: GraphGradientMode.None },
{ label: 'Opacity', value: GraphGradientMode.Opacity, description: 'Enable fill opacity gradient' },
{ label: 'Hue', value: GraphGradientMode.Hue, description: 'Small color hue gradient' },
{
label: 'Scheme',
label: t('grafana-ui.u-plot.config.get-graph-field-options.fill-gradient.label-none', 'None'),
value: GraphGradientMode.None,
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.fill-gradient.label-opacity', 'Opacity'),
value: GraphGradientMode.Opacity,
description: t(
'grafana-ui.u-plot.config.get-graph-field-options.fill-gradient.description-opacity',
'Enable fill opacity gradient'
),
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.fill-gradient.label-hue', 'Hue'),
value: GraphGradientMode.Hue,
description: t(
'grafana-ui.u-plot.config.get-graph-field-options.fill-gradient.description-hue',
'Small color hue gradient'
),
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.fill-gradient.label-scheme', 'Scheme'),
value: GraphGradientMode.Scheme,
description: 'Use color scheme to define gradient',
description: t(
'grafana-ui.u-plot.config.get-graph-field-options.fill-gradient.description-scheme',
'Use color scheme to define gradient'
),
},
],
stacking: [
{ label: 'Off', value: StackingMode.None },
{ label: 'Normal', value: StackingMode.Normal },
{ label: '100%', value: StackingMode.Percent },
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.stacking.label-off', 'Off'),
value: StackingMode.None,
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.stacking.label-normal', 'Normal'),
value: StackingMode.Normal,
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.stacking.label-100', '100%'),
value: StackingMode.Percent,
},
],
thresholdsDisplayModes: [
{ label: 'Off', value: GraphThresholdsStyleMode.Off },
{ label: 'As lines', value: GraphThresholdsStyleMode.Line },
{ label: 'As lines (dashed)', value: GraphThresholdsStyleMode.Dashed },
{ label: 'As filled regions', value: GraphThresholdsStyleMode.Area },
{ label: 'As filled regions and lines', value: GraphThresholdsStyleMode.LineAndArea },
{ label: 'As filled regions and lines (dashed)', value: GraphThresholdsStyleMode.DashedAndArea },
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.thresholds-display-mode.label-off', 'Off'),
value: GraphThresholdsStyleMode.Off,
},
{
label: t('grafana-ui.u-plot.config.get-graph-field-options.thresholds-display-mode.label-lines', 'As lines'),
value: GraphThresholdsStyleMode.Line,
},
{
label: t(
'grafana-ui.u-plot.config.get-graph-field-options.thresholds-display-mode.label-dashed-lines',
'As lines (dashed)'
),
value: GraphThresholdsStyleMode.Dashed,
},
{
label: t(
'grafana-ui.u-plot.config.get-graph-field-options.thresholds-display-mode.label-filled-regions',
'As filled regions'
),
value: GraphThresholdsStyleMode.Area,
},
{
label: t(
'grafana-ui.u-plot.config.get-graph-field-options.thresholds-display-mode.label-filled-regions-and-lines',
'As filled regions and lines'
),
value: GraphThresholdsStyleMode.LineAndArea,
},
{
label: t(
'grafana-ui.u-plot.config.get-graph-field-options.thresholds-display-mode.label-filled-regions-and-dashed-lines',
'As filled regions and lines (dashed)'
),
value: GraphThresholdsStyleMode.DashedAndArea,
},
],
};
});
/**
* @deprecated Use `getGraphFieldOptions` instead so translations load correctly.
*/
export const graphFieldOptions = getGraphFieldOptions();

@ -341,7 +341,7 @@ const LegacyForms = {
export { LegacyForms, LegacyInputStatus };
// WIP, need renames and exports cleanup
export { graphFieldOptions } from './components/uPlot/config';
export { graphFieldOptions, getGraphFieldOptions } from './components/uPlot/config';
export { UPlotConfigBuilder } from './components/uPlot/config/UPlotConfigBuilder';
export { UPLOT_AXIS_FONT_SIZE } from './components/uPlot/config/UPlotAxisBuilder';
export { UPlotChart } from './components/uPlot/Plot';

@ -13,9 +13,7 @@ import { RadioButtonGroup } from '../../components/Forms/RadioButtonGroup/RadioB
import { Input } from '../../components/Input/Input';
import { Stack } from '../../components/Layout/Stack/Stack';
import { Select } from '../../components/Select/Select';
import { graphFieldOptions } from '../../components/uPlot/config';
const category = ['Axis'];
import { getGraphFieldOptions } from '../../components/uPlot/config';
/**
* @alpha
@ -23,14 +21,15 @@ const category = ['Axis'];
export function addAxisConfig(builder: FieldConfigEditorBuilder<AxisConfig>, defaultConfig: AxisConfig) {
// options for axis appearance
addAxisPlacement(builder);
const category = [t('grafana-ui.builder.axis.category-axis', 'Axis')];
builder.addTextInput({
path: 'axisLabel',
name: 'Label',
name: t('grafana-ui.builder.axis.name-label', 'Label'),
category,
defaultValue: '',
settings: {
placeholder: 'Optional text',
placeholder: t('grafana-ui.builder.axis.placeholder-label', 'Optional text'),
expandTemplateVars: true,
},
showIf: (c) => c.axisPlacement !== AxisPlacement.Hidden,
@ -43,34 +42,34 @@ export function addAxisConfig(builder: FieldConfigEditorBuilder<AxisConfig>, def
builder
.addRadio({
path: 'axisGridShow',
name: 'Show grid lines',
name: t('grafana-ui.builder.axis.name-grid-lines', 'Show grid lines'),
category,
defaultValue: undefined,
settings: {
options: [
{ value: undefined, label: 'Auto' },
{ value: true, label: 'On' },
{ value: false, label: 'Off' },
{ value: undefined, label: t('grafana-ui.builder.axis.grid-line-options.label-auto', 'Auto') },
{ value: true, label: t('grafana-ui.builder.axis.grid-line-options.label-on', 'On') },
{ value: false, label: t('grafana-ui.builder.axis.grid-line-options.label-off', 'Off') },
],
},
showIf: (c) => c.axisPlacement !== AxisPlacement.Hidden,
})
.addRadio({
path: 'axisColorMode',
name: 'Color',
name: t('grafana-ui.builder.axis.color-label', 'Color'),
category,
defaultValue: AxisColorMode.Text,
settings: {
options: [
{ value: AxisColorMode.Text, label: 'Text' },
{ value: AxisColorMode.Series, label: 'Series' },
{ value: AxisColorMode.Text, label: t('grafana-ui.builder.axis.color-options.label-text', 'Text') },
{ value: AxisColorMode.Series, label: t('grafana-ui.builder.axis.color-options.label-series', 'Series') },
],
},
showIf: (c) => c.axisPlacement !== AxisPlacement.Hidden,
})
.addBooleanSwitch({
path: 'axisBorderShow',
name: 'Show border',
name: t('grafana-ui.builder.axis.name-show-border', 'Show border'),
category,
defaultValue: false,
showIf: (c) => c.axisPlacement !== AxisPlacement.Hidden,
@ -81,7 +80,7 @@ export function addAxisConfig(builder: FieldConfigEditorBuilder<AxisConfig>, def
.addCustomEditor<void, ScaleDistributionConfig>({
id: 'scaleDistribution',
path: 'scaleDistribution',
name: 'Scale',
name: t('grafana-ui.builder.axis.name-scale', 'Scale'),
category,
editor: ScaleDistributionEditor,
override: ScaleDistributionEditor,
@ -91,46 +90,31 @@ export function addAxisConfig(builder: FieldConfigEditorBuilder<AxisConfig>, def
})
.addBooleanSwitch({
path: 'axisCenteredZero',
name: 'Centered zero',
name: t('grafana-ui.builder.axis.name-centered-zero', 'Centered zero'),
category,
defaultValue: false,
showIf: (c) => c.scaleDistribution?.type !== ScaleDistribution.Log,
})
.addNumberInput({
path: 'axisSoftMin',
name: 'Soft min',
name: t('grafana-ui.builder.axis.name-soft-min', 'Soft min'),
defaultValue: defaultConfig.axisSoftMin,
category,
settings: {
placeholder: 'See: Standard options > Min',
placeholder: t('grafana-ui.builder.axis.placeholder-soft-min', 'See: Standard options > Min'),
},
})
.addNumberInput({
path: 'axisSoftMax',
name: 'Soft max',
name: t('grafana-ui.builder.axis.name-soft-max', 'Soft max'),
defaultValue: defaultConfig.axisSoftMax,
category,
settings: {
placeholder: 'See: Standard options > Max',
placeholder: t('grafana-ui.builder.axis.placeholder-soft-max', 'See: Standard options > Max'),
},
});
}
const DISTRIBUTION_OPTIONS: Array<SelectableValue<ScaleDistribution>> = [
{
label: 'Linear',
value: ScaleDistribution.Linear,
},
{
label: 'Logarithmic',
value: ScaleDistribution.Log,
},
{
label: 'Symlog',
value: ScaleDistribution.Symlog,
},
];
const LOG_DISTRIBUTION_OPTIONS: Array<SelectableValue<number>> = [
{
label: '2',
@ -148,6 +132,20 @@ const LOG_DISTRIBUTION_OPTIONS: Array<SelectableValue<number>> = [
export const ScaleDistributionEditor = ({ value, onChange }: StandardEditorProps<ScaleDistributionConfig>) => {
const type = value?.type ?? ScaleDistribution.Linear;
const log = value?.log ?? 2;
const DISTRIBUTION_OPTIONS: Array<SelectableValue<ScaleDistribution>> = [
{
label: t('grafana-ui.builder.axis.scale-distribution-editor.distribution-options.label-linear', 'Linear'),
value: ScaleDistribution.Linear,
},
{
label: t('grafana-ui.builder.axis.scale-distribution-editor.distribution-options.label-log', 'Logarithmic'),
value: ScaleDistribution.Log,
},
{
label: t('grafana-ui.builder.axis.scale-distribution-editor.distribution-options.label-symlog', 'Symlog'),
value: ScaleDistribution.Symlog,
},
];
return (
<Stack direction="column" gap={2}>
@ -199,10 +197,10 @@ export const ScaleDistributionEditor = ({ value, onChange }: StandardEditorProps
export function addAxisWidth(builder: FieldConfigEditorBuilder<AxisConfig>) {
builder.addNumberInput({
path: 'axisWidth',
name: 'Width',
category,
name: t('grafana-ui.builder.axis.name-width', 'Width'),
category: [t('grafana-ui.builder.axis.category-axis', 'Axis')],
settings: {
placeholder: 'Auto',
placeholder: t('grafana-ui.builder.axis.placeholder-width', 'Auto'),
},
showIf: (c) => c.axisPlacement !== AxisPlacement.Hidden,
});
@ -213,10 +211,11 @@ export function addAxisPlacement(
builder: FieldConfigEditorBuilder<AxisConfig>,
optionsFilter = (placement: AxisPlacement) => true
) {
const graphFieldOptions = getGraphFieldOptions();
builder.addRadio({
path: 'axisPlacement',
name: 'Placement',
category,
name: t('grafana-ui.builder.axis.name-placement', 'Placement'),
category: [t('grafana-ui.builder.axis.category-axis', 'Axis')],
defaultValue: graphFieldOptions.axisPlacement[0].value,
settings: {
options: graphFieldOptions.axisPlacement.filter((placement) => optionsFilter(placement.value!)),

@ -1,4 +1,5 @@
import { PanelOptionsEditorBuilder, standardEditorsRegistry, StatsPickerConfigSettings } from '@grafana/data';
import { t } from '@grafana/i18n';
import { LegendDisplayMode, OptionsWithLegend } from '@grafana/schema';
/**
@ -9,46 +10,47 @@ export function addLegendOptions<T extends OptionsWithLegend>(
includeLegendCalcs = true,
showLegend = true
) {
const category = [t('grafana-ui.builder.legend.category', 'Legend')];
builder
.addBooleanSwitch({
path: 'legend.showLegend',
name: 'Visibility',
category: ['Legend'],
name: t('grafana-ui.builder.legend.name-visibility', 'Visibility'),
category,
description: '',
defaultValue: showLegend,
})
.addRadio({
path: 'legend.displayMode',
name: 'Mode',
category: ['Legend'],
name: t('grafana-ui.builder.legend.name-mode', 'Mode'),
category,
description: '',
defaultValue: LegendDisplayMode.List,
settings: {
options: [
{ value: LegendDisplayMode.List, label: 'List' },
{ value: LegendDisplayMode.Table, label: 'Table' },
{ value: LegendDisplayMode.List, label: t('grafana-ui.builder.legend.mode-options.label-list', 'List') },
{ value: LegendDisplayMode.Table, label: t('grafana-ui.builder.legend.mode-options.label-table', 'Table') },
],
},
showIf: (c) => c.legend.showLegend,
})
.addRadio({
path: 'legend.placement',
name: 'Placement',
category: ['Legend'],
name: t('grafana-ui.builder.legend.name-placement', 'Placement'),
category,
description: '',
defaultValue: 'bottom',
settings: {
options: [
{ value: 'bottom', label: 'Bottom' },
{ value: 'right', label: 'Right' },
{ value: 'bottom', label: t('grafana-ui.builder.legend.placement-options.label-bottom', 'Bottom') },
{ value: 'right', label: t('grafana-ui.builder.legend.placement-options.label-right', 'Right') },
],
},
showIf: (c) => c.legend.showLegend,
})
.addNumberInput({
path: 'legend.width',
name: 'Width',
category: ['Legend'],
name: t('grafana-ui.builder.legend.name-width', 'Width'),
category,
settings: {
placeholder: 'Auto',
},
@ -59,9 +61,9 @@ export function addLegendOptions<T extends OptionsWithLegend>(
builder.addCustomEditor<StatsPickerConfigSettings, string[]>({
id: 'legend.calcs',
path: 'legend.calcs',
name: 'Values',
category: ['Legend'],
description: 'Select values or calculations to show in legend',
name: t('grafana-ui.builder.legend.name-values', 'Values'),
category,
description: t('grafana-ui.builder.legend.description-values', 'Select values or calculations to show in legend'),
editor: standardEditorsRegistry.get('stats-picker').editor,
defaultValue: [],
settings: {

@ -12,7 +12,7 @@ import { RadioButtonGroup } from '../../components/Forms/RadioButtonGroup/RadioB
import { IconButton } from '../../components/IconButton/IconButton';
import { Input } from '../../components/Input/Input';
import { Stack } from '../../components/Layout/Stack/Stack';
import { graphFieldOptions } from '../../components/uPlot/config';
import { getGraphFieldOptions } from '../../components/uPlot/config';
export const StackingEditor = ({
value,
@ -61,10 +61,11 @@ export function addStackingConfig(
defaultConfig?: StackingConfig,
category = ['Graph styles']
) {
const graphFieldOptions = getGraphFieldOptions();
builder.addCustomEditor({
id: 'stacking',
path: 'stacking',
name: 'Stack series',
name: t('grafana-ui.builder.stacking.name-stack-series', 'Stack series'),
category: category,
defaultValue: defaultConfig,
editor: StackingEditor,

@ -1,4 +1,5 @@
import { PanelOptionsEditorBuilder } from '@grafana/data';
import { t } from '@grafana/i18n';
import { OptionsWithTextFormatting } from '@grafana/schema';
/**
@ -11,13 +12,14 @@ export function addTextSizeOptions<T extends OptionsWithTextFormatting>(
builder: PanelOptionsEditorBuilder<T>,
withTitle = true
) {
const category = [t('grafana-ui.builder.text.category-text-size', 'Text size')];
if (withTitle) {
builder.addNumberInput({
path: 'text.titleSize',
category: ['Text size'],
name: 'Title',
category,
name: t('grafana-ui.builder.text.name-title', 'Title'),
settings: {
placeholder: 'Auto',
placeholder: t('grafana-ui.builder.text.placeholder-title', 'Auto'),
integer: false,
min: 1,
max: 200,
@ -28,10 +30,10 @@ export function addTextSizeOptions<T extends OptionsWithTextFormatting>(
builder.addNumberInput({
path: 'text.valueSize',
category: ['Text size'],
name: 'Value',
category,
name: t('grafana-ui.builder.text.name-value', 'Value'),
settings: {
placeholder: 'Auto',
placeholder: t('grafana-ui.builder.text.placeholder-value', 'Auto'),
integer: false,
min: 1,
max: 200,
@ -41,10 +43,10 @@ export function addTextSizeOptions<T extends OptionsWithTextFormatting>(
builder.addNumberInput({
path: 'text.percentSize',
category: ['Text size'],
name: 'Percent change',
category,
name: t('grafana-ui.builder.text.name-percent-change', 'Percent change'),
settings: {
placeholder: 'Auto',
placeholder: t('grafana-ui.builder.text.placeholder-percent-change', 'Auto'),
integer: false,
min: 1,
max: 200,

@ -1,4 +1,5 @@
import { DataFrame, PanelOptionsEditorBuilder } from '@grafana/data';
import { t } from '@grafana/i18n';
import { OptionsWithTooltip, TooltipDisplayMode, SortOrder } from '@grafana/schema';
/** @internal */
@ -16,28 +17,28 @@ export function addTooltipOptions<T extends OptionsWithTooltip>(
setProximity = false,
defaultOptions?: Partial<OptionsWithTooltip>
) {
const category = ['Tooltip'];
const category = [t('grafana-ui.builder.tooltip.category', 'Tooltip')];
const modeOptions = singleOnly
? [
{ value: TooltipDisplayMode.Single, label: 'Single' },
{ value: TooltipDisplayMode.None, label: 'Hidden' },
{ value: TooltipDisplayMode.Single, label: t('grafana-ui.builder.tooltip.modeOptions.label-single', 'Single') },
{ value: TooltipDisplayMode.None, label: t('grafana-ui.builder.tooltip.modeOptions.label-hidden', 'Hidden') },
]
: [
{ value: TooltipDisplayMode.Single, label: 'Single' },
{ value: TooltipDisplayMode.Multi, label: 'All' },
{ value: TooltipDisplayMode.None, label: 'Hidden' },
{ value: TooltipDisplayMode.Single, label: t('grafana-ui.builder.tooltip.modeOptions.label-single', 'Single') },
{ value: TooltipDisplayMode.Multi, label: t('grafana-ui.builder.tooltip.modeOptions.label-all', 'All') },
{ value: TooltipDisplayMode.None, label: t('grafana-ui.builder.tooltip.modeOptions.label-hidden', 'Hidden') },
];
const sortOptions = [
{ value: SortOrder.None, label: 'None' },
{ value: SortOrder.Ascending, label: 'Ascending' },
{ value: SortOrder.Descending, label: 'Descending' },
{ value: SortOrder.None, label: t('grafana-ui.builder.tooltip.sortOptions.label-none', 'None') },
{ value: SortOrder.Ascending, label: t('grafana-ui.builder.tooltip.sortOptions.label-ascending', 'Ascending') },
{ value: SortOrder.Descending, label: t('grafana-ui.builder.tooltip.sortOptions.label-descending', 'Descending') },
];
builder
.addRadio({
path: 'tooltip.mode',
name: 'Tooltip mode',
name: t('grafana-ui.builder.tooltip.name-tooltip-mode', 'Tooltip mode'),
category,
defaultValue: defaultOptions?.tooltip?.mode ?? TooltipDisplayMode.Single,
settings: {
@ -46,7 +47,7 @@ export function addTooltipOptions<T extends OptionsWithTooltip>(
})
.addRadio({
path: 'tooltip.sort',
name: 'Values sort order',
name: t('grafana-ui.builder.tooltip.name-values-sort-order', 'Values sort order'),
category,
defaultValue: defaultOptions?.tooltip?.sort ?? SortOrder.None,
showIf: (options: T) => options.tooltip?.mode === TooltipDisplayMode.Multi,
@ -56,7 +57,7 @@ export function addTooltipOptions<T extends OptionsWithTooltip>(
})
.addBooleanSwitch({
path: 'tooltip.hideZeros',
name: 'Hide zeros',
name: t('grafana-ui.builder.tooltip.name-hide-zeros', 'Hide zeros'),
category,
defaultValue: false,
showIf: (options: T) =>
@ -66,8 +67,11 @@ export function addTooltipOptions<T extends OptionsWithTooltip>(
if (setProximity) {
builder.addNumberInput({
path: 'tooltip.hoverProximity',
name: 'Hover proximity',
description: 'How close the cursor must be to a point to trigger the tooltip, in pixels',
name: t('grafana-ui.builder.tooltip.name-hover-proximity', 'Hover proximity'),
description: t(
'grafana-ui.builder.tooltip.description-hover-proximity',
'How close the cursor must be to a point to trigger the tooltip, in pixels'
),
category,
settings: {
integer: true,
@ -79,7 +83,7 @@ export function addTooltipOptions<T extends OptionsWithTooltip>(
builder
.addNumberInput({
path: 'tooltip.maxWidth',
name: 'Max width',
name: t('grafana-ui.builder.tooltip.name-max-width', 'Max width'),
category,
settings: {
integer: true,
@ -88,7 +92,7 @@ export function addTooltipOptions<T extends OptionsWithTooltip>(
})
.addNumberInput({
path: 'tooltip.maxHeight',
name: 'Max height',
name: t('grafana-ui.builder.tooltip.name-max-height', 'Max height'),
category,
defaultValue: undefined,
settings: {

@ -2,6 +2,7 @@ import { css } from '@emotion/react';
import { GrafanaTheme2, ThemeTypographyVariant } from '@grafana/data';
import { getFeatureToggle } from '../../utils/featureToggle';
import { getFocusStyles } from '../mixins';
export function getElementStyles(theme: GrafanaTheme2, isExtensionSidebarOpen?: boolean) {
@ -61,6 +62,7 @@ export function getElementStyles(theme: GrafanaTheme2, isExtensionSidebarOpen?:
fontVariantLigatures: 'no-contextual',
...theme.typography.body,
...bodyOverflow,
fontVariantNumeric: getFeatureToggle('tabularNumbers') ? 'tabular-nums' : 'initial',
},
'h1, .h1': getVariantStyles(theme.typography.h1),

@ -4,7 +4,7 @@ go 1.24.4
require (
github.com/emicklei/go-restful/v3 v3.11.0
github.com/grafana/grafana-plugin-sdk-go v0.277.0
github.com/grafana/grafana-plugin-sdk-go v0.278.0
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e
github.com/grafana/grafana/pkg/semconv v0.0.0-20250514132646-acbc7b54ed9e
github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38
@ -25,13 +25,13 @@ require (
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/apache/arrow-go/v18 v18.2.0 // indirect
github.com/apache/arrow-go/v18 v18.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
@ -53,7 +53,6 @@ require (
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.25.0 // indirect
github.com/google/flatbuffers v25.2.10+incompatible // indirect
@ -65,7 +64,7 @@ require (
github.com/grafana/otel-profiling-go v0.5.1 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
@ -125,15 +124,14 @@ require (
go.etcd.io/etcd/client/v3 v3.5.21 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
@ -150,7 +148,6 @@ require (
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.34.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect

@ -11,8 +11,8 @@ github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7X
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/apache/arrow-go/v18 v18.2.0 h1:QhWqpgZMKfWOniGPhbUxrHohWnooGURqL2R2Gg4SO1Q=
github.com/apache/arrow-go/v18 v18.2.0/go.mod h1:Ic/01WSwGJWRrdAZcxjBZ5hbApNJ28K96jGYaxzzGUc=
github.com/apache/arrow-go/v18 v18.3.0 h1:Xq4A6dZj9Nu33sqZibzn012LNnewkTUlfKVUFD/RX/I=
github.com/apache/arrow-go/v18 v18.3.0/go.mod h1:eEM1DnUTHhgGAjf/ChvOAQbUQ+EPohtDrArffvUjPg8=
github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE=
github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@ -30,8 +30,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 h1:VnjHsRXCRti7Av7E+j4DCha3kf68echfDzQ+wD11SBU=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 h1:UZdrvid2JFwnvPlUSEFlE794XZL4Jmrj8fuxfcLECJE=
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
@ -134,8 +134,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/grafana/grafana-plugin-sdk-go v0.277.0 h1:VDU2F4Y5NeRS//ejctdZtsAshrGaEdbtW33FsK0EQss=
github.com/grafana/grafana-plugin-sdk-go v0.277.0/go.mod h1:mAUWg68w5+1f5TLDqagIr8sWr1RT9h7ufJl5NMcWJAU=
github.com/grafana/grafana-plugin-sdk-go v0.278.0 h1:5/rIYparLi02pofdaag8wnjspMMVNCi8cZhC4cdC3Ho=
github.com/grafana/grafana-plugin-sdk-go v0.278.0/go.mod h1:+8NXT/XUJ/89GV6FxGQ366NZ3nU+cAXDMd0OUESF9H4=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:IA4SOwun8QyST9c5UNs/fN37XL6boXXDvRYFcFwbipg=
github.com/grafana/grafana/pkg/semconv v0.0.0-20250514132646-acbc7b54ed9e h1:vheR6iPO1np+G/ARjcWx9yiWd7BnTDgyTgsnMhOvx70=
@ -146,8 +146,8 @@ github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKt
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20191002090509-6af20e3a5340 h1:uGoIog/wiQHI9GAxXO5TJbT0wWKH3O9HhOJW1F9c3fY=
@ -357,12 +357,12 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 h1:lREC4C0ilyP4WibDhQ7Gg2ygAQFP8oR07Fst/5cafwI=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0/go.mod h1:HfvuU0kW9HewH14VCOLImqKvUgONodURG7Alj/IrnGI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 h1:SoCgXYF4ISDtNyfLUzsGDaaudZVTx2yJhOyBO0+/GYk=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0/go.mod h1:VHu48l0YTRKSObdPQ+Sb8xMZvdnJlN7yhHuHoPgNqHM=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 h1:bQ1Gvah4Sp8z7epSkgJaNTuZm7sutfA6Fji2/7cKFMc=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0/go.mod h1:9b8Q9rH52NgYH3ShiTFB5wf18Vt3RTH/VMB7LDcC1ug=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=

@ -64,13 +64,12 @@ require (
go.etcd.io/etcd/client/v3 v3.5.21 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
go.opentelemetry.io/proto/otlp v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect

@ -188,8 +188,8 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0=

@ -9,7 +9,7 @@ require (
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e
github.com/prometheus/client_golang v1.22.0
github.com/stretchr/testify v1.10.0
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0
go.opentelemetry.io/otel v1.36.0
go.opentelemetry.io/otel/trace v1.36.0
k8s.io/apimachinery v0.33.1
@ -73,7 +73,7 @@ require (
go.etcd.io/etcd/client/v3 v3.5.21 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect

@ -201,10 +201,10 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 h1:SoCgXYF4ISDtNyfLUzsGDaaudZVTx2yJhOyBO0+/GYk=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0/go.mod h1:VHu48l0YTRKSObdPQ+Sb8xMZvdnJlN7yhHuHoPgNqHM=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0=

@ -0,0 +1,22 @@
# Pa11y accessability tests
We use pa11y to run some automated simple accessability tests. They're ran with dagger to help orchestrate starting server + tests in a reproducable manner.
To run the tests locally:
1. Install dagger locally https://docs.dagger.io/install/
2. Grab the grafana.tar.gz artifact by either
1. Downloading it from the Github Action artifact from your PR
1. Build it locally with:
```sh
dagger run go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir="$PWD" > dist/files.txt
cat dist/files.txt # Will output the path to the grafana.tar.gz
```
3. Run the dagger pipeline with:
```sh
dagger -v run go run ./pkg/build/a11y --package=(full path to .tar.gz) --results=./pa11y-ci-results.json
```
The JSON results file will be saved to the file from the `--results` arg
4. If they fail and you want to see the full output
1. Run the dagger command with `dagger -vE [...]`
2. At the end, arrow up to the exec pa11y-ci segment and hit Enter

@ -6,12 +6,16 @@ import (
"log"
"os"
"os/signal"
"path/filepath"
"dagger.io/dagger"
"github.com/urfave/cli/v3"
)
var (
grafanaHost = "grafana"
grafanaPort = 3001
)
func main() {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
@ -49,8 +53,21 @@ func NewApp() *cli.Command {
TakesFile: true,
},
&cli.StringFlag{
Name: "flags",
Usage: "Flags to pass through to the e2e runner",
Name: "config",
Usage: "Path to the pa11y config file to use",
Value: "e2e/pa11yci.conf.js",
Validator: mustBeFile("config", true),
TakesFile: true,
},
&cli.StringFlag{
Name: "results",
Usage: "Path to the pa11y results file to export",
TakesFile: true,
},
&cli.BoolFlag{
Name: "no-threshold-fail",
Usage: "Don't fail the task if any of the tests fail. Use this in combination with --results to list all violations even if they're within thresholds",
Value: false,
},
},
Action: run,
@ -61,26 +78,29 @@ func run(ctx context.Context, cmd *cli.Command) error {
grafanaDir := cmd.String("grafana-dir")
targzPath := cmd.String("package")
licensePath := cmd.String("license")
runnerFlags := cmd.String("flags")
pa11yConfigPath := cmd.String("config")
pa11yResultsPath := cmd.String("results")
noThresholdFail := cmd.Bool("no-threshold-fail")
d, err := dagger.Connect(ctx)
if err != nil {
return fmt.Errorf("failed to connect to Dagger: %w", err)
}
yarnCache := d.CacheVolume("yarn")
//nolint:gosec
nvmrcContents, err := os.ReadFile(filepath.Join(grafanaDir, ".nvmrc"))
if err != nil {
return fmt.Errorf("failed to read .nvmrc file: %w", err)
}
nodeVersion := string(nvmrcContents)
grafana := d.Host().Directory(grafanaDir, dagger.HostDirectoryOpts{
Exclude: []string{"node_modules", "*.tar.gz"},
// Explicitly only the files used by the grafana-server service
hostSrc := d.Host().Directory(grafanaDir, dagger.HostDirectoryOpts{
Include: []string{
"./devenv",
"./e2e/test-plugins", // Directory is included so provisioning works, but they're not actually build
"./scripts/grafana-server/custom.ini",
"./scripts/grafana-server/start-server",
"./scripts/grafana-server/kill-server",
"./scripts/grafana-server/variables",
},
})
targz := d.Host().File(targzPath)
pa11yConfig := d.Host().File(pa11yConfigPath)
var license *dagger.File
if licensePath != "" {
@ -88,27 +108,34 @@ func run(ctx context.Context, cmd *cli.Command) error {
}
svc, err := GrafanaService(ctx, d, GrafanaServiceOpts{
GrafanaDir: grafana,
HostSrc: hostSrc,
GrafanaTarGz: targz,
License: license,
YarnCache: yarnCache,
NodeVersion: nodeVersion,
})
if err != nil {
return fmt.Errorf("failed to create Grafana service: %w", err)
}
c := RunTest(d, svc, grafana, yarnCache, nodeVersion, runnerFlags)
c, err = c.Sync(ctx)
if err != nil {
return fmt.Errorf("failed to run a11y test suite: %w", err)
c, runErr := RunTest(ctx, d, svc, pa11yConfig, noThresholdFail, pa11yResultsPath)
if runErr != nil {
return fmt.Errorf("failed to run a11y test suite: %w", runErr)
}
code, err := c.ExitCode(ctx)
if err != nil {
return fmt.Errorf("failed to get exit code of a11y test suite: %w", err)
c, syncErr := c.Sync(ctx)
if syncErr != nil {
return fmt.Errorf("failed to sync a11y test suite: %w", syncErr)
}
code, codeErr := c.ExitCode(ctx)
if codeErr != nil {
return fmt.Errorf("failed to get exit code of a11y test suite: %w", codeErr)
}
if code != 0 {
if code == 0 {
log.Printf("a11y tests passed with exit code %d", code)
} else if noThresholdFail {
log.Printf("a11y tests failed with exit code %d, but noFail is true", code)
} else {
return fmt.Errorf("a11y tests failed with exit code %d", code)
}

@ -1,25 +1,48 @@
package main
import (
"context"
"fmt"
"dagger.io/dagger"
)
func RunTest(
ctx context.Context,
d *dagger.Client,
svc *dagger.Service,
src *dagger.Directory, cache *dagger.CacheVolume,
nodeVersion, runnerFlags string) *dagger.Container {
command := fmt.Sprintf(
"./e2e-runner a11y --start-grafana=false"+
" --grafana-host grafana --grafana-port 3001 %s", runnerFlags)
return GrafanaFrontend(d, cache, nodeVersion, src).
WithExec([]string{"/bin/sh", "-c", "apt-get update && apt-get install -y git curl"}).
WithExec([]string{"curl", "-LO", "https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb"}).
WithExec([]string{"apt-get", "install", "-y", "./google-chrome-stable_current_amd64.deb"}).
grafanaService *dagger.Service,
pa11yConfig *dagger.File,
noThresholdFail bool,
pa11yResultsPath string,
) (*dagger.Container, error) {
// docker-puppeteer container already has Chrome and Pa11y installed in it
pa11yContainer := d.Container().From("grafana/docker-puppeteer:1.1.0").
WithWorkdir("/src").
WithServiceBinding("grafana", svc).
WithExec([]string{"/bin/bash", "-c", command}, dagger.ContainerWithExecOpts{Expect: dagger.ReturnTypeAny})
WithExec([]string{"mkdir", "-p", "./screenshots"}). // not yet exported
WithEnvVariable("HOST", grafanaHost).
WithEnvVariable("PORT", fmt.Sprint(grafanaPort))
if noThresholdFail {
// This logic is non-intuitive - --no-threshold-fail will make pa11y fail (by removing thresholds from the config)
// so it can write all violations to the results file. This failure is then ignored by the caller in main.go.
// Otherwise, pa11y ignores violations if they're within the thresholds and doesn't include them in the results file
pa11yContainer = pa11yContainer.
WithEnvVariable("NO_THRESHOLDS", "true")
}
pa11yContainer = pa11yContainer.
WithServiceBinding(grafanaHost, grafanaService).
WithMountedFile("pa11yci-config.js", pa11yConfig).
WithExec([]string{"pa11y-ci", "--config", "pa11yci-config.js"}, dagger.ContainerWithExecOpts{
Expect: dagger.ReturnTypeAny, // allow this to fail here so we can handle non-zero exit codes at the caller
})
if pa11yResultsPath != "" {
_, err := pa11yContainer.File("/src/pa11y-ci-results.json").Export(ctx, pa11yResultsPath)
if err != nil {
return nil, fmt.Errorf("failed to get pa11y results: %w", err)
}
}
return pa11yContainer, nil
}

@ -9,77 +9,31 @@ import (
"dagger.io/dagger"
)
func NodeImage(version string) string {
return fmt.Sprintf("node:%s-slim", strings.TrimPrefix(strings.TrimSpace(version), "v"))
}
type GrafanaServiceOpts struct {
GrafanaDir *dagger.Directory
HostSrc *dagger.Directory
GrafanaTarGz *dagger.File
License *dagger.File
YarnCache *dagger.CacheVolume
NodeVersion string
}
func Frontend(src *dagger.Directory) *dagger.Directory {
return src.
WithoutFile("go.mod").
WithoutFile("go.sum").
WithoutFile("go.work").
WithoutFile("go.work.sum").
WithoutDirectory(".github").
WithoutDirectory("docs").
WithoutDirectory("pkg").
WithoutDirectory("apps").
WithoutDirectory("videos")
}
func WithGrafanaFrontend(c *dagger.Container, src *dagger.Directory) *dagger.Container {
return c.WithDirectory("/src", Frontend(src), dagger.ContainerWithDirectoryOpts{
Exclude: []string{
"*drone*",
"*.go",
"*.md",
},
})
}
func WithYarnCache(c *dagger.Container, cache *dagger.CacheVolume) *dagger.Container {
return c.
WithWorkdir("/src").
WithMountedCache("/yarn/cache", cache)
}
func GrafanaFrontend(d *dagger.Client, yarnCache *dagger.CacheVolume, nodeVersion string, grafanaDir *dagger.Directory) *dagger.Container {
container := d.Container().From(NodeImage(nodeVersion))
container = WithGrafanaFrontend(container, grafanaDir)
return WithYarnCache(container, yarnCache).
WithEnvVariable("YARN_CACHE_FOLDER", "/yarn/cache").
WithExec([]string{"yarn", "install", "--immutable"})
}
func GrafanaService(ctx context.Context, d *dagger.Client, opts GrafanaServiceOpts) (*dagger.Service, error) {
src := GrafanaFrontend(d, opts.YarnCache, opts.NodeVersion, opts.GrafanaDir)
container := d.Container().From("alpine:3").
WithExec([]string{"apk", "add", "--no-cache", "bash", "tar", "netcat-openbsd"}).
WithMountedFile("/src/grafana.tar.gz", opts.GrafanaTarGz).
WithExec([]string{"mkdir", "-p", "/src/grafana"}).
WithExec([]string{"tar", "--strip-components=1", "-xzf", "/src/grafana.tar.gz", "-C", "/src/grafana"}).
WithDirectory("/src/grafana/devenv", src.Directory("/src/devenv")).
WithDirectory("/src/grafana/e2e", src.Directory("/src/e2e")).
WithDirectory("/src/grafana/scripts", src.Directory("/src/scripts")).
WithDirectory("/src/grafana/tools", src.Directory("/src/tools")).
WithDirectory("/src/grafana/devenv", opts.HostSrc.Directory("./devenv")).
WithDirectory("/src/grafana/e2e/test-plugins", opts.HostSrc.Directory("./e2e/test-plugins")).
WithDirectory("/src/grafana/scripts", opts.HostSrc.Directory("./scripts")).
WithWorkdir("/src/grafana").
WithEnvVariable("GF_APP_MODE", "development").
WithEnvVariable("GF_SERVER_HTTP_PORT", "3001").
WithEnvVariable("GF_SERVER_HTTP_PORT", fmt.Sprint(grafanaPort)).
WithEnvVariable("GF_SERVER_ROUTER_LOGGING", "1").
WithExposedPort(3001)
WithExposedPort(grafanaPort)
var licenseArg string
if opts.License != nil {
container = container.WithMountedFile("/src/license.jwt", opts.License)
licenseArg = "/src/license.jwt"
container = container.WithMountedFile(licenseArg, opts.License)
}
// We add all GF_ environment variables to allow for overriding Grafana configuration.

@ -67,7 +67,7 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
golang.org/x/sys v0.33.0 // indirect

@ -267,8 +267,8 @@ go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF
go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.12.2 h1:06ZeJRe5BnYXceSM9Vya83XXVaNGe3H1QqsvqRANQq8=

@ -77,19 +77,19 @@ func ToUnifiedStorage(c utils.CommandLine, cfg *setting.Cfg, sqlStore db.DB) err
sort.ProvideService(),
)
if c.Bool("non-interactive") {
client, err := newUnifiedClient(cfg, sqlStore)
if err != nil {
return err
}
client, err := newUnifiedClient(cfg, sqlStore)
if err != nil {
return err
}
if c.Bool("non-interactive") {
opts.Store = client
opts.BlobStore = client
rsp, err := migrator.Migrate(ctx, opts)
if err != nil {
msg := fmt.Sprintf("Failed to migrate legacy resources: %+v", err)
return cli.Exit(msg, 1)
if exitErr := handleMigrationError(err, rsp); exitErr != nil {
return exitErr
}
logger.Info("Migrated legacy resources successfully in", time.Since(start))
if rsp != nil {
jj, _ := json.MarshalIndent(rsp, "", " ")
@ -154,11 +154,6 @@ func ToUnifiedStorage(c utils.CommandLine, cfg *setting.Cfg, sqlStore db.DB) err
return err
}
if yes {
client, err := newUnifiedClient(cfg, sqlStore)
if err != nil {
return err
}
// Check the stats (eventually compare)
req := &resourcepb.ResourceStatsRequest{
Namespace: opts.Namespace,
@ -219,9 +214,14 @@ func promptYesNo(prompt string) (bool, error) {
}
func newUnifiedClient(cfg *setting.Cfg, sqlStore db.DB) (resource.ResourceClient, error) {
featureManager, err := featuremgmt.ProvideManagerService(cfg)
if err != nil {
return nil, err
}
featureToggles := featuremgmt.ProvideToggles(featureManager)
return unified.ProvideUnifiedStorageClient(&unified.Options{
Cfg: cfg,
Features: featuremgmt.WithFeatures(), // none??
Features: featureToggles,
DB: sqlStore,
Tracer: tracing.NewNoopTracerService(),
Reg: prometheus.NewPedanticRegistry(),
@ -238,3 +238,19 @@ func newParquetClient(file *os.File) (resourcepb.BulkStoreClient, error) {
client := parquet.NewBulkResourceWriterClient(writer)
return client, nil
}
func handleMigrationError(err error, rsp *resourcepb.BulkResponse) error {
if err != nil {
return cli.Exit(fmt.Sprintf("Failed to migrate legacy resources: %+v", err), 1)
}
if rsp != nil && rsp.Error != nil {
msg := fmt.Sprintf("Failed to migrate legacy resources: %s", rsp.Error.Message)
if rsp.Error.Reason != "" {
msg += fmt.Sprintf(" (%s)", rsp.Error.Reason)
}
return cli.Exit(msg, 1)
}
return nil
}

@ -45,7 +45,7 @@ var (
datasourceResponseGauge = promauto.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: "plugins",
Namespace: "grafana",
Name: "datasource_response_size",
Help: "gauge of external data source response sizes returned to Grafana in bytes",
}, []string{"datasource", "datasource_type", "secure_socks_ds_proxy_enabled"},

@ -4,7 +4,7 @@ go 1.24.4
require (
github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914
github.com/grafana/grafana-plugin-sdk-go v0.277.0
github.com/grafana/grafana-plugin-sdk-go v0.278.0
github.com/json-iterator/go v1.1.12
github.com/prometheus/client_golang v1.22.0
github.com/prometheus/common v0.64.0
@ -20,7 +20,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/apache/arrow-go/v18 v18.2.0 // indirect
github.com/apache/arrow-go/v18 v18.3.0 // indirect
github.com/aws/aws-sdk-go v1.55.7 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@ -28,7 +28,7 @@ require (
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 // indirect
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dennwc/varint v1.0.0 // indirect
@ -48,7 +48,6 @@ require (
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/flatbuffers v25.2.10+incompatible // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect
@ -57,7 +56,7 @@ require (
github.com/grafana/otel-profiling-go v0.5.1 // indirect
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
@ -105,8 +104,8 @@ require (
go.mongodb.org/mongo-driver v1.16.1 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
@ -122,7 +121,6 @@ require (
golang.org/x/text v0.26.0 // indirect
golang.org/x/tools v0.34.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
gonum.org/v1/gonum v0.16.0 // indirect
google.golang.org/api v0.229.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect

@ -21,8 +21,8 @@ github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vS
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/apache/arrow-go/v18 v18.2.0 h1:QhWqpgZMKfWOniGPhbUxrHohWnooGURqL2R2Gg4SO1Q=
github.com/apache/arrow-go/v18 v18.2.0/go.mod h1:Ic/01WSwGJWRrdAZcxjBZ5hbApNJ28K96jGYaxzzGUc=
github.com/apache/arrow-go/v18 v18.3.0 h1:Xq4A6dZj9Nu33sqZibzn012LNnewkTUlfKVUFD/RX/I=
github.com/apache/arrow-go/v18 v18.3.0/go.mod h1:eEM1DnUTHhgGAjf/ChvOAQbUQ+EPohtDrArffvUjPg8=
github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE=
github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
@ -45,8 +45,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 h1:VnjHsRXCRti7Av7E+j4DCha3kf68echfDzQ+wD11SBU=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4 h1:UZdrvid2JFwnvPlUSEFlE794XZL4Jmrj8fuxfcLECJE=
github.com/chromedp/cdproto v0.0.0-20250429231605-6ed5b53462d4/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
@ -135,16 +135,16 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914 h1:qcSGhr691f1mmPHwg2svGyO40Ex92G02aOyHzP6XHCE=
github.com/grafana/dskit v0.0.0-20250611075409-46f51e1ce914/go.mod h1:OiN4P4aC6LwLzLbEupH3Ue83VfQoNMfG48rsna8jI/E=
github.com/grafana/grafana-plugin-sdk-go v0.277.0 h1:VDU2F4Y5NeRS//ejctdZtsAshrGaEdbtW33FsK0EQss=
github.com/grafana/grafana-plugin-sdk-go v0.277.0/go.mod h1:mAUWg68w5+1f5TLDqagIr8sWr1RT9h7ufJl5NMcWJAU=
github.com/grafana/grafana-plugin-sdk-go v0.278.0 h1:5/rIYparLi02pofdaag8wnjspMMVNCi8cZhC4cdC3Ho=
github.com/grafana/grafana-plugin-sdk-go v0.278.0/go.mod h1:+8NXT/XUJ/89GV6FxGQ366NZ3nU+cAXDMd0OUESF9H4=
github.com/grafana/otel-profiling-go v0.5.1 h1:stVPKAFZSa7eGiqbYuG25VcqYksR6iWvF3YH66t4qL8=
github.com/grafana/otel-profiling-go v0.5.1/go.mod h1:ftN/t5A/4gQI19/8MoWurBEtC6gFw8Dns1sJZ9W4Tls=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248=
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o=
github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
@ -316,12 +316,12 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0 h1:UIrZgRBHUrYRlJ4V419lVb4rs2ar0wFzKNAebaP05XU=
go.opentelemetry.io/contrib/propagators/jaeger v1.35.0/go.mod h1:0ciyFyYZxE6JqRAQvIgGRabKWDUmNdW3GAQb6y/RlFU=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 h1:lREC4C0ilyP4WibDhQ7Gg2ygAQFP8oR07Fst/5cafwI=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0/go.mod h1:HfvuU0kW9HewH14VCOLImqKvUgONodURG7Alj/IrnGI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0 h1:SoCgXYF4ISDtNyfLUzsGDaaudZVTx2yJhOyBO0+/GYk=
go.opentelemetry.io/contrib/propagators/jaeger v1.36.0/go.mod h1:VHu48l0YTRKSObdPQ+Sb8xMZvdnJlN7yhHuHoPgNqHM=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0 h1:bQ1Gvah4Sp8z7epSkgJaNTuZm7sutfA6Fji2/7cKFMc=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.30.0/go.mod h1:9b8Q9rH52NgYH3ShiTFB5wf18Vt3RTH/VMB7LDcC1ug=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save