mirror of https://github.com/grafana/grafana.git
merge main
This commit is contained in:
commit
7b38c5078b
42
.drone.yml
42
.drone.yml
|
@ -134,7 +134,7 @@ steps:
|
|||
environment:
|
||||
HOST: start-storybook
|
||||
PORT: "9001"
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-storybook-suite
|
||||
trigger:
|
||||
event:
|
||||
|
@ -561,7 +561,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-dashboards-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite old-arch/dashboards-suite
|
||||
|
@ -570,7 +570,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-old-arch/dashboards-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite smoke-tests-suite
|
||||
|
@ -579,7 +579,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-smoke-tests-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite old-arch/smoke-tests-suite
|
||||
|
@ -588,7 +588,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-old-arch/smoke-tests-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite panels-suite
|
||||
|
@ -597,7 +597,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-panels-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite old-arch/panels-suite
|
||||
|
@ -606,7 +606,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-old-arch/panels-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite various-suite
|
||||
|
@ -615,7 +615,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-various-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite old-arch/various-suite
|
||||
|
@ -624,7 +624,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-old-arch/various-suite
|
||||
- commands:
|
||||
- GITHUB_TOKEN=$(cat /github-app/token)
|
||||
|
@ -1658,7 +1658,7 @@ steps:
|
|||
environment:
|
||||
HOST: start-storybook
|
||||
PORT: "9001"
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-storybook-suite
|
||||
trigger:
|
||||
branch: main
|
||||
|
@ -1851,7 +1851,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-dashboards-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite old-arch/dashboards-suite
|
||||
|
@ -1860,7 +1860,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-old-arch/dashboards-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite smoke-tests-suite
|
||||
|
@ -1869,7 +1869,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-smoke-tests-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite old-arch/smoke-tests-suite
|
||||
|
@ -1878,7 +1878,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-old-arch/smoke-tests-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite panels-suite
|
||||
|
@ -1887,7 +1887,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-panels-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite old-arch/panels-suite
|
||||
|
@ -1896,7 +1896,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-old-arch/panels-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite various-suite
|
||||
|
@ -1905,7 +1905,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-various-suite
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite old-arch/various-suite
|
||||
|
@ -1914,7 +1914,7 @@ steps:
|
|||
- build-test-plugins
|
||||
environment:
|
||||
HOST: grafana-server
|
||||
image: cypress/included:13.10.0
|
||||
image: cypress/included:14.3.2
|
||||
name: end-to-end-tests-old-arch/various-suite
|
||||
- commands:
|
||||
- GITHUB_TOKEN=$(cat /github-app/token)
|
||||
|
@ -4923,7 +4923,7 @@ steps:
|
|||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/drone-downstream
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/docker-puppeteer:1.1.0
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/docs-base:latest
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM cypress/included:13.10.0
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM cypress/included:14.3.2
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM jwilder/dockerize:0.6.1
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM koalaman/shellcheck:stable
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM rockylinux:9
|
||||
|
@ -4961,7 +4961,7 @@ steps:
|
|||
- trivy --exit-code 1 --severity HIGH,CRITICAL grafana/drone-downstream
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL grafana/docker-puppeteer:1.1.0
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL grafana/docs-base:latest
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL cypress/included:13.10.0
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL cypress/included:14.3.2
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL jwilder/dockerize:0.6.1
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL koalaman/shellcheck:stable
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL rockylinux:9
|
||||
|
@ -5210,6 +5210,6 @@ kind: secret
|
|||
name: gcr_credentials
|
||||
---
|
||||
kind: signature
|
||||
hmac: 8e25f1f786b8de4eb21dfbeca8c5fcb9701b1e62ecf98287d9225ecc6e8c29e8
|
||||
hmac: 16029e3922ae0a13a31233717aa172c06bf0e6fc8cf01f5148de62147c259ac8
|
||||
|
||||
...
|
||||
|
|
|
@ -138,6 +138,7 @@
|
|||
/pkg/services/dashboardversion/ @grafana/grafana-backend-group
|
||||
/pkg/services/encryption/ @grafana/grafana-operator-experience-squad
|
||||
/pkg/services/folder/ @grafana/grafana-search-and-storage
|
||||
/pkg/services/frontend/ @grafana/grafana-frontend-platform
|
||||
/pkg/services/apiserver @grafana/grafana-app-platform-squad
|
||||
/pkg/services/hooks/ @grafana/grafana-backend-group
|
||||
/pkg/services/kmsproviders/ @grafana/grafana-operator-experience-squad
|
||||
|
@ -435,6 +436,7 @@
|
|||
/packages/grafana-ui/src/graveyard/GraphNG/ @grafana/dataviz-squad
|
||||
/packages/grafana-ui/src/graveyard/TimeSeries/ @grafana/dataviz-squad
|
||||
/packages/grafana-ui/src/utils/storybook/ @grafana/grafana-frontend-platform
|
||||
/packages/grafana-alerting/ @grafana/alerting-frontend
|
||||
|
||||
# root files, mostly frontend
|
||||
/.browserslistrc @grafana/frontend-ops
|
||||
|
|
|
@ -6,6 +6,10 @@ on:
|
|||
- main
|
||||
- release-*.*.*
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
lint-frontend-verify-i18n:
|
||||
name: Verify i18n
|
||||
|
@ -30,6 +34,9 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
lint-frontend-prettier:
|
||||
# Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`,
|
||||
# the `lint-frontend-prettier-enterprise` workflow will run instead
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -42,7 +49,29 @@ jobs:
|
|||
- run: yarn install --immutable --check-cache
|
||||
- run: yarn run prettier:check
|
||||
- run: yarn run lint
|
||||
lint-frontend-prettier-enterprise:
|
||||
# Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks)
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'yarn.lock'
|
||||
- name: Setup Enterprise
|
||||
uses: ./.github/actions/setup-enterprise
|
||||
with:
|
||||
github-app-name: 'grafana-ci-bot'
|
||||
- run: yarn install --immutable --check-cache
|
||||
- run: yarn run prettier:check
|
||||
- run: yarn run lint
|
||||
lint-frontend-typecheck:
|
||||
# Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`,
|
||||
# the `lint-frontend-typecheck-enterprise` workflow will run instead
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true
|
||||
name: Typecheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -54,6 +83,24 @@ jobs:
|
|||
cache-dependency-path: 'yarn.lock'
|
||||
- run: yarn install --immutable --check-cache
|
||||
- run: yarn run typecheck
|
||||
lint-frontend-typecheck-enterprise:
|
||||
# Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks)
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
|
||||
name: Typecheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'yarn.lock'
|
||||
- name: Setup Enterprise
|
||||
uses: ./.github/actions/setup-enterprise
|
||||
with:
|
||||
github-app-name: 'grafana-ci-bot'
|
||||
- run: yarn install --immutable --check-cache
|
||||
- run: yarn run typecheck
|
||||
lint-frontend-betterer:
|
||||
name: Betterer
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
@ -6,8 +6,15 @@ on:
|
|||
- main
|
||||
- release-*.*.*
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
frontend-unit-tests:
|
||||
# Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`,
|
||||
# the `frontend-unit-tests-enterprise` workflow will run instead
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
name: "Unit tests (${{ matrix.chunk }} / 8)"
|
||||
strategy:
|
||||
|
@ -27,3 +34,30 @@ jobs:
|
|||
TEST_MAX_WORKERS: 2
|
||||
TEST_SHARD: ${{ matrix.chunk }}
|
||||
TEST_SHARD_TOTAL: 8
|
||||
|
||||
frontend-unit-tests-enterprise:
|
||||
# Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks)
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
name: "Unit tests (${{ matrix.chunk }} / 8)"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
chunk: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'yarn.lock'
|
||||
- name: Setup Enterprise
|
||||
uses: ./.github/actions/setup-enterprise
|
||||
with:
|
||||
github-app-name: 'grafana-ci-bot'
|
||||
- run: yarn install --immutable --check-cache
|
||||
- run: yarn run test:ci
|
||||
env:
|
||||
TEST_MAX_WORKERS: 2
|
||||
TEST_SHARD: ${{ matrix.chunk }}
|
||||
TEST_SHARD_TOTAL: 8
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
name: Dispatch check for patch conflicts
|
||||
run-name: dispatch-check-patch-conflicts-${{ github.base_ref }}-${{ github.head_ref }}
|
||||
on:
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
|
@ -26,7 +26,6 @@ jobs:
|
|||
# App needs Actions: Read/Write for the grafana/security-patch-actions repo
|
||||
app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }}
|
||||
private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }}
|
||||
|
||||
- name: "Dispatch job"
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
|
|
|
@ -23,22 +23,9 @@ jobs:
|
|||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
- name: Restore GOCACHE
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
key: go-test-cache-${{ github.ref_name }}-sqlite
|
||||
restore-keys: |
|
||||
go-test-cache-${{ github.base_ref }}-sqlite
|
||||
go-test-cache-main-sqlite
|
||||
path: /home/runner/.cache/go-build
|
||||
- run: |
|
||||
make gen-go
|
||||
go test -tags=sqlite -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\(.*\)/' | sort -u)
|
||||
- name: Save GOCACHE
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
key: go-test-cache-${{ github.ref_name }}-sqlite
|
||||
path: /home/runner/.cache/go-build
|
||||
mysql:
|
||||
name: MySQL
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
|
@ -64,24 +51,11 @@ jobs:
|
|||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
- name: Restore GOCACHE
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
key: go-test-cache-${{ github.ref_name }}-mysql
|
||||
restore-keys: |
|
||||
go-test-cache-${{ github.base_ref }}-mysql
|
||||
go-test-cache-main-mysql
|
||||
path: /home/runner/.cache/go-build
|
||||
- run: |
|
||||
sudo apt-get update -yq && sudo apt-get install mariadb-client
|
||||
cat devenv/docker/blocks/mysql_tests/setup.sql | mariadb -h 127.0.0.1 -P 3306 -u root -prootpass --disable-ssl-verify-server-cert
|
||||
make gen-go
|
||||
go test -tags=mysql -p=1 -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\(.*\)/' | sort -u)
|
||||
- name: Save GOCACHE
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
key: go-test-cache-${{ github.ref_name }}-mysql
|
||||
path: /home/runner/.cache/go-build
|
||||
postgres:
|
||||
name: Postgres
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
|
@ -102,14 +76,6 @@ jobs:
|
|||
with:
|
||||
go-version-file: go.mod
|
||||
cache: true
|
||||
- name: Restore GOCACHE
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
key: go-test-cache-${{ github.ref_name }}-postgres
|
||||
restore-keys: |
|
||||
go-test-cache-${{ github.base_ref }}-postgres
|
||||
go-test-cache-main-postgres
|
||||
path: /home/runner/.cache/go-build
|
||||
- env:
|
||||
GRAFANA_TEST_DB: postgres
|
||||
PGPASSWORD: grafanatest
|
||||
|
@ -119,8 +85,3 @@ jobs:
|
|||
psql -p 5432 -h 127.0.0.1 -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql
|
||||
make gen-go
|
||||
go test -p=1 -tags=postgres -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\(.*\)/' | sort -u)
|
||||
- name: Save GOCACHE
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
key: go-test-cache-${{ github.ref_name }}-postgres
|
||||
path: /home/runner/.cache/go-build
|
||||
|
|
|
@ -82,6 +82,16 @@
|
|||
"cwd": "${workspaceFolder}",
|
||||
"args": ["server", "target", "--homepath", "${workspaceFolder}", "--packaging", "dev"]
|
||||
},
|
||||
{
|
||||
"name": "Run Frontend Server",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/pkg/cmd/grafana/",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": { "GF_DEFAULT_TARGET": "frontend-server", "GF_SERVER_HTTP_PORT": "3003" },
|
||||
"args": ["server", "target", "--homepath", "${workspaceFolder}", "--packaging", "dev"]
|
||||
},
|
||||
{
|
||||
"name": "Attach to Chrome",
|
||||
"port": 9222,
|
||||
|
|
|
@ -70,7 +70,6 @@ COPY .citools/swagger .citools/swagger
|
|||
|
||||
# Include vendored dependencies
|
||||
COPY pkg/util/xorm pkg/util/xorm
|
||||
COPY pkg/apis/folder pkg/apis/folder
|
||||
COPY pkg/apis/secret pkg/apis/secret
|
||||
COPY pkg/apiserver pkg/apiserver
|
||||
COPY pkg/apimachinery pkg/apimachinery
|
||||
|
|
|
@ -189,7 +189,16 @@ func runStepsInParallel(ctx context.Context, spec *advisorv0alpha1.CheckSpec, st
|
|||
go func(step checks.Step, item any) {
|
||||
defer wg.Done()
|
||||
defer func() { <-limit }()
|
||||
stepErr, err := step.Run(ctx, spec, item)
|
||||
var stepErr *advisorv0alpha1.CheckReportFailure
|
||||
var err error
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("panic recovered in step %s: %v", step.ID(), r)
|
||||
}
|
||||
}()
|
||||
stepErr, err = step.Run(ctx, spec, item)
|
||||
}()
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if err != nil {
|
||||
|
|
|
@ -134,14 +134,6 @@ func TestProcessCheck_RunError(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestProcessCheck_IgnoreSteps(t *testing.T) {
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{})
|
||||
meta, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
checkType := &advisorv0alpha1.CheckType{}
|
||||
checkType.SetAnnotations(map[string]string{checks.IgnoreStepsAnnotation: "mock"})
|
||||
typesClient := &mockTypesClient{
|
||||
|
@ -152,6 +144,14 @@ func TestProcessCheck_IgnoreSteps(t *testing.T) {
|
|||
items: []any{"item"},
|
||||
err: errors.New("run error, should not be triggered"),
|
||||
}
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{})
|
||||
meta, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
|
||||
err = processCheck(ctx, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
|
@ -159,6 +159,29 @@ func TestProcessCheck_IgnoreSteps(t *testing.T) {
|
|||
assert.Equal(t, "mock", obj.GetAnnotations()[checks.IgnoreStepsAnnotation])
|
||||
}
|
||||
|
||||
func TestProcessCheck_RunRecoversFromPanic(t *testing.T) {
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{})
|
||||
meta, err := utils.MetaAccessor(obj)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meta.SetCreatedBy("user:1")
|
||||
client := &mockClient{}
|
||||
typesClient := &mockTypesClient{}
|
||||
ctx := context.TODO()
|
||||
|
||||
check := &mockCheck{
|
||||
items: []any{"item"},
|
||||
runPanics: true,
|
||||
}
|
||||
|
||||
err = processCheck(ctx, client, typesClient, obj, check)
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "panic recovered in step")
|
||||
assert.Equal(t, "error", obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
}
|
||||
|
||||
func TestProcessCheckRetry_NoRetry(t *testing.T) {
|
||||
obj := &advisorv0alpha1.Check{}
|
||||
obj.SetAnnotations(map[string]string{})
|
||||
|
@ -257,8 +280,9 @@ func (m *mockTypesClient) Get(ctx context.Context, id resource.Identifier) (reso
|
|||
}
|
||||
|
||||
type mockCheck struct {
|
||||
err error
|
||||
items []any
|
||||
err error
|
||||
items []any
|
||||
runPanics bool
|
||||
}
|
||||
|
||||
func (m *mockCheck) ID() string {
|
||||
|
@ -275,15 +299,19 @@ func (m *mockCheck) Item(ctx context.Context, id string) (any, error) {
|
|||
|
||||
func (m *mockCheck) Steps() []checks.Step {
|
||||
return []checks.Step{
|
||||
&mockStep{err: m.err},
|
||||
&mockStep{err: m.err, panics: m.runPanics},
|
||||
}
|
||||
}
|
||||
|
||||
type mockStep struct {
|
||||
err error
|
||||
err error
|
||||
panics bool
|
||||
}
|
||||
|
||||
func (m *mockStep) Run(ctx context.Context, obj *advisorv0alpha1.CheckSpec, items any) (*advisorv0alpha1.CheckReportFailure, error) {
|
||||
if m.panics {
|
||||
panic("panic")
|
||||
}
|
||||
if m.err != nil {
|
||||
return nil, m.err
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ AnnotationPanelFilter: {
|
|||
exclude?: bool | *false
|
||||
|
||||
// Panel IDs that should be included or excluded
|
||||
ids: [...uint8]
|
||||
ids: [...uint32]
|
||||
}
|
||||
|
||||
// "Off" for no shared crosshair or tooltip (default).
|
||||
|
|
|
@ -294,8 +294,6 @@ var _ resource.ListObject = &DashboardList{}
|
|||
|
||||
// Copy methods for all subresource types
|
||||
|
||||
|
||||
|
||||
// DeepCopy creates a full deep copy of DashboardStatus
|
||||
func (s *DashboardStatus) DeepCopy() *DashboardStatus {
|
||||
cpy := &DashboardStatus{}
|
||||
|
|
|
@ -294,8 +294,6 @@ var _ resource.ListObject = &DashboardList{}
|
|||
|
||||
// Copy methods for all subresource types
|
||||
|
||||
|
||||
|
||||
// DeepCopy creates a full deep copy of DashboardStatus
|
||||
func (s *DashboardStatus) DeepCopy() *DashboardStatus {
|
||||
cpy := &DashboardStatus{}
|
||||
|
|
|
@ -82,7 +82,7 @@ AnnotationPanelFilter: {
|
|||
exclude?: bool | *false
|
||||
|
||||
// Panel IDs that should be included or excluded
|
||||
ids: [...uint8]
|
||||
ids: [...uint32]
|
||||
}
|
||||
|
||||
// "Off" for no shared crosshair or tooltip (default).
|
||||
|
|
|
@ -73,7 +73,7 @@ type DashboardAnnotationPanelFilter struct {
|
|||
// Should the specified panels be included or excluded
|
||||
Exclude *bool `json:"exclude,omitempty"`
|
||||
// Panel IDs that should be included or excluded
|
||||
Ids []uint8 `json:"ids"`
|
||||
Ids []uint32 `json:"ids"`
|
||||
}
|
||||
|
||||
// NewDashboardAnnotationPanelFilter creates a new DashboardAnnotationPanelFilter object.
|
||||
|
|
|
@ -544,7 +544,7 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardAnnotationPanelFilter(ref commo
|
|||
SchemaProps: spec.SchemaProps{
|
||||
Default: 0,
|
||||
Type: []string{"integer"},
|
||||
Format: "byte",
|
||||
Format: "int64",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -3,6 +3,7 @@ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/
|
|||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAdhocVariableSpec,BaseFilters
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAdhocVariableSpec,DefaultKeys
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAdhocVariableSpec,Filters
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAnnotationPanelFilter,Ids
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardAutoGridLayoutSpec,Items
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardConditionalRenderingGroupSpec,Items
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardCustomVariableSpec,Options
|
||||
|
|
|
@ -11,8 +11,6 @@ import (
|
|||
"github.com/grafana/grafana-app-sdk/app"
|
||||
)
|
||||
|
||||
var ()
|
||||
|
||||
var appManifestData = app.ManifestData{
|
||||
AppName: "dashboard",
|
||||
Group: "dashboard.grafana.app",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
aliases:
|
||||
- administration/cli/
|
||||
description: Guide to using grafana cli
|
||||
description: Guide to using grafana server cli
|
||||
keywords:
|
||||
- grafana
|
||||
- cli
|
||||
|
@ -11,15 +11,15 @@ labels:
|
|||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Grafana CLI
|
||||
title: Grafana server CLI
|
||||
weight: 400
|
||||
---
|
||||
|
||||
# Grafana CLI
|
||||
# Grafana server CLI
|
||||
|
||||
Grafana CLI is a small executable that's bundled with Grafana server.
|
||||
Grafana server CLI is a small executable that's bundled with Grafana server.
|
||||
You can run it on the same machine Grafana server is running on.
|
||||
Grafana CLI has `plugins` and `admin` commands, as well as global options.
|
||||
Grafana server CLI has `plugins` and `admin` commands, as well as global options.
|
||||
|
||||
To list all commands and options:
|
||||
|
||||
|
@ -27,9 +27,9 @@ To list all commands and options:
|
|||
grafana cli -h
|
||||
```
|
||||
|
||||
## Run Grafana CLI
|
||||
## Run Grafana server CLI
|
||||
|
||||
To run Grafana CLI, add the path to the Grafana binaries in your `PATH` environment variable.
|
||||
To run Grafana server CLI, add the path to the Grafana binaries in your `PATH` environment variable.
|
||||
Alternately, if your current directory is the `bin` directory, run `./grafana cli`.
|
||||
Otherwise, you can specify full path to the binary.
|
||||
For example, on Linux `/usr/share/grafana/bin/grafana` and on Windows `C:\Program Files\GrafanaLabs\grafana\bin\grafana.exe`, and run it with `grafana cli`.
|
||||
|
@ -41,7 +41,7 @@ If you're on Windows, run Windows PowerShell as Administrator.
|
|||
|
||||
## Grafana CLI command syntax
|
||||
|
||||
The general syntax for commands in Grafana CLI is:
|
||||
The general syntax for commands in Grafana server CLI is:
|
||||
|
||||
```bash
|
||||
grafana cli [global options] command [command options] [arguments...]
|
||||
|
@ -49,11 +49,11 @@ grafana cli [global options] command [command options] [arguments...]
|
|||
|
||||
## Global options
|
||||
|
||||
Grafana CLI allows you to temporarily override certain Grafana default settings. Except for `--help` and `--version`, most global options are only used by developers.
|
||||
Grafana server CLI allows you to temporarily override certain Grafana default settings. Except for `--help` and `--version`, most global options are only used by developers.
|
||||
|
||||
Each global option applies only to the command in which it is used. For example, `--pluginsDir value` does not permanently change where Grafana saves plugins. It only changes it for command in which you apply the option.
|
||||
|
||||
### Display Grafana CLI help
|
||||
### Display Grafana server CLI help
|
||||
|
||||
`--help` or `-h` displays the help, including default paths and Docker configuration information.
|
||||
|
||||
|
@ -63,9 +63,9 @@ Each global option applies only to the command in which it is used. For example,
|
|||
grafana cli -h
|
||||
```
|
||||
|
||||
### Display Grafana CLI version
|
||||
### Display Grafana server CLI version
|
||||
|
||||
`--version` or `-v` prints the version of Grafana CLI currently running.
|
||||
`--version` or `-v` prints the version of Grafana server CLI currently running.
|
||||
|
||||
**Example:**
|
||||
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
aliases:
|
||||
- ../../http_api/new_api_structure/
|
||||
- ../../http_api/new_api_structure/
|
||||
canonical: /docs/grafana/latest/developers/http_api/new_api_structure/
|
||||
description: ''
|
||||
keywords:
|
||||
- grafana
|
||||
- http
|
||||
- documentation
|
||||
- api
|
||||
labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: New API Structure
|
||||
---
|
||||
|
||||
# Grafana's New API Structure
|
||||
|
||||
## Overview
|
||||
|
||||
Going forward, Grafana's HTTP API will follow a standardized API structure alongside consistent API versioning.
|
||||
|
||||
## API Path Structure
|
||||
|
||||
All Grafana APIs follow this standardized format:
|
||||
|
||||
```
|
||||
/apis/<group>/<version>/namespaces/<namespace>/<resource>[/<name>]
|
||||
```
|
||||
|
||||
Where the final `/<name>` segment is used for operations on individual resources (like Get, Update, Delete) and omitted for collection operations (like List, Create).
|
||||
|
||||
## Understanding the Components
|
||||
|
||||
### Group (`<group>`)
|
||||
|
||||
Groups organize related functionality into logical collections. For example `dashboard.grafana.app` will be used for all dashboard-related operations.
|
||||
|
||||
### Version (`<version>`)
|
||||
|
||||
These APIs will also uses semantic versioning with three stability levels:
|
||||
|
||||
| Level | Format | Description | Use Case |
|
||||
| ----- | ---------- | --------------------------------------------------------------------------- | ------------------------ |
|
||||
| Alpha | `v1alpha1` | Early development stage. Unstable, may contain bugs, and subject to removal | For testing new features |
|
||||
| Beta | `v1beta1` | More stable than alpha, but may still have some changes | For early production use |
|
||||
| GA | `v1` | Generally Available. Stable with backward compatibility guarantees | For production use |
|
||||
|
||||
### Namespace (`<namespace>`)
|
||||
|
||||
Namespaces isolate resources within your Grafana instance. The format varies by deployment type:
|
||||
|
||||
#### OSS & On-Premise Grafana
|
||||
|
||||
- Default organization (org 1): `default`
|
||||
- Additional organizations: `org-<org_id>`
|
||||
|
||||
#### Grafana Cloud
|
||||
|
||||
- Format: `stacks-<stack_id>`
|
||||
- Your instance ID is the `stack_id`. You can find this value by either:
|
||||
- Going to grafana.com, clicking on your stack, and selecting "Details" on your Grafana instance
|
||||
- Accessing the /swagger page in your cloud instance, where the namespace will be automatically populated on the relevant endpoints
|
||||
|
||||
### Resource (`<resource>`)
|
||||
|
||||
Represents the core resource you want to interact with, such as:
|
||||
|
||||
- `dashboards`
|
||||
- `playlists`
|
||||
- `folders`
|
||||
|
||||
### Name (<name>)
|
||||
|
||||
The `<name>` is the unique identifier for a specific instance of a resource within its namespace and resource type. `<name>` is distinct from the metadata.uid field. The URL path will always use the metadata.name.
|
||||
|
||||
For example, to get a dashboard defined as:
|
||||
|
||||
```
|
||||
{
|
||||
"kind": "Dashboard",
|
||||
"apiVersion": "dashboard.grafana.app/v1beta1",
|
||||
"metadata": {
|
||||
"name": "production-overview", // This value IS used in the URL path
|
||||
"namespace": "default",
|
||||
"uid": "a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8" // This value is NOT used in the URL path
|
||||
// ... other metadata
|
||||
},
|
||||
"spec": {
|
||||
// ... dashboard spec
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You would use the following API call:
|
||||
|
||||
`GET /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards/production-overview`
|
|
@ -16,22 +16,859 @@ labels:
|
|||
title: Dashboard HTTP API
|
||||
---
|
||||
|
||||
# Dashboard API
|
||||
# New Dashboard APIs
|
||||
|
||||
> If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions](/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes/) for more information.
|
||||
|
||||
## Identifier (id) vs unique identifier (uid)
|
||||
> To view more about the new api structure, refer to [API overview]({{< ref "apis" >}}).
|
||||
|
||||
The identifier (id) of a dashboard is an auto-incrementing numeric value and is only unique per Grafana install.
|
||||
## Create Dashboard
|
||||
|
||||
The unique identifier (uid) of a dashboard can be used for uniquely identify a dashboard between multiple Grafana installs.
|
||||
`POST /apis/dashboard.grafana.app/v1beta1/namespaces/:namespace/dashboards`
|
||||
|
||||
Creates a new dashboard.
|
||||
|
||||
- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}).
|
||||
|
||||
**Required permissions**
|
||||
|
||||
See note in the [introduction]({{< ref "#dashboard-api" >}}) for an explanation.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
| Action | Scope |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `dashboards:create` | <ul><li>`folders:*`</li><li>`folders:uid:*`</li></ul> |
|
||||
| `dashboards:write` | <ul><li>`dashboards:*`</li><li>`dashboards:uid:*`</li><li>`folders:*`</li><li>`folders:uid:*`</li></ul> |
|
||||
{ .no-spacing-list }
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
**Example Create Request**:
|
||||
|
||||
```http
|
||||
POST /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"metadata": {
|
||||
"name": "gdxccn",
|
||||
"annotations": {
|
||||
"grafana.app/folder": "fef30w4jaxla8b"
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "grafana"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": false,
|
||||
"iconColor": "red",
|
||||
"name": "Example annotation",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
}
|
||||
}]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [
|
||||
{
|
||||
"asDropdown": false,
|
||||
"icon": "external link",
|
||||
"includeVars": false,
|
||||
"keepTime": false,
|
||||
"tags": [],
|
||||
"targetBlank": false,
|
||||
"title": "Example Link",
|
||||
"tooltip": "",
|
||||
"type": "dashboards",
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "grafana"
|
||||
},
|
||||
"description": "With a description",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"barWidthFactor": 0.6,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"hideZeros": false,
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "12.0.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "grafana"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Example panel",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"preload": false,
|
||||
"schemaVersion": 41,
|
||||
"tags": ["example"],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": {
|
||||
"text": "",
|
||||
"value": ""
|
||||
},
|
||||
"definition": "",
|
||||
"description": "example description",
|
||||
"label": "ExampleLabel",
|
||||
"name": "ExampleVariable",
|
||||
"options": [],
|
||||
"query": "",
|
||||
"refresh": 1,
|
||||
"regex": "cluster",
|
||||
"type": "query"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "browser",
|
||||
"title": "Example Dashboard",
|
||||
"version": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
JSON Body schema:
|
||||
|
||||
- **metadata.name** – The Grafana [unique identifier]({{< ref "#identifier-id-vs-unique-identifier-uid" >}}). If you do not want to provide this, set metadata.generateName instead to the prefix you would like for the randomly generated uid (cannot be an empty string).
|
||||
- **metadata.annotations.grafana.app/folder** - Optional field, the unique identifier of the folder under which the dashboard should be created.
|
||||
- **spec** – The dashboard json.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
Content-Length: 485
|
||||
|
||||
{
|
||||
"kind": "Dashboard",
|
||||
"apiVersion": "dashboard.grafana.app/v1beta1",
|
||||
"metadata": {
|
||||
"name": "gdxccn",
|
||||
"namespace": "default",
|
||||
"uid": "Cc7fA5ffHY94NnHZyMxXvFlpFtOmkK3qkBcVZPKSPXcX",
|
||||
"resourceVersion": "1",
|
||||
"generation": 1,
|
||||
"creationTimestamp": "2025-04-24T20:35:29Z",
|
||||
"labels": {
|
||||
"grafana.app/deprecatedInternalID": "11"
|
||||
},
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "service-account:dejwtrofg77y8d",
|
||||
"grafana.app/folder": "fef30w4jaxla8b"
|
||||
},
|
||||
"managedFields": [
|
||||
{
|
||||
"manager": "curl",
|
||||
"operation": "Update",
|
||||
"apiVersion": "dashboard.grafana.app/v0alpha1",
|
||||
"time": "2025-04-24T20:35:29Z",
|
||||
"fieldsType": "FieldsV1",
|
||||
"fieldsV1": {
|
||||
"f:spec": {
|
||||
"f:annotations": {
|
||||
".": {},
|
||||
"f:list": {}
|
||||
},
|
||||
"f:editable": {},
|
||||
"f:fiscalYearStartMonth": {},
|
||||
"f:graphTooltip": {},
|
||||
"f:links": {},
|
||||
"f:panels": {},
|
||||
"f:preload": {},
|
||||
"f:schemaVersion": {},
|
||||
"f:tags": {},
|
||||
"f:templating": {
|
||||
".": {},
|
||||
"f:list": {}
|
||||
},
|
||||
"f:time": {
|
||||
".": {},
|
||||
"f:from": {},
|
||||
"f:to": {}
|
||||
},
|
||||
"f:timepicker": {},
|
||||
"f:timezone": {},
|
||||
"f:title": {},
|
||||
"f:version": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"spec": {
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "grafana"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": false,
|
||||
"iconColor": "red",
|
||||
"name": "Example annotation",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [
|
||||
{
|
||||
"asDropdown": false,
|
||||
"icon": "external link",
|
||||
"includeVars": false,
|
||||
"keepTime": false,
|
||||
"tags": [],
|
||||
"targetBlank": false,
|
||||
"title": "Example Link",
|
||||
"tooltip": "",
|
||||
"type": "dashboards",
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "grafana"
|
||||
},
|
||||
"description": "With a description",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"barWidthFactor": 0.6,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green"
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"hideZeros": false,
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "12.0.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "grafana"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Example panel",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"preload": false,
|
||||
"schemaVersion": 41,
|
||||
"tags": [
|
||||
"example"
|
||||
],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": {
|
||||
"text": "",
|
||||
"value": ""
|
||||
},
|
||||
"definition": "",
|
||||
"description": "example description",
|
||||
"label": "ExampleLabel",
|
||||
"name": "ExampleVariable",
|
||||
"options": [],
|
||||
"query": "",
|
||||
"refresh": 1,
|
||||
"regex": "cluster",
|
||||
"type": "query"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "browser",
|
||||
"title": "Example Dashboard"
|
||||
},
|
||||
"status": {}
|
||||
```
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **201** – Created
|
||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
||||
- **401** – Unauthorized
|
||||
- **403** – Access denied
|
||||
- **409** – Conflict (dashboard with the same uid already exists)
|
||||
|
||||
## Update Dashboard
|
||||
|
||||
`PUT /apis/dashboard.grafana.app/v1beta1/namespaces/:namespace/dashboards/:uid`
|
||||
|
||||
Updates an existing dashboard via the dashboard uid.
|
||||
|
||||
- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}).
|
||||
- uid: the unique identifier of the dashboard to update. this will be the _name_ in the dashboard response
|
||||
|
||||
**Required permissions**
|
||||
|
||||
See note in the [introduction]({{< ref "#dashboard-api" >}}) for an explanation.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
| Action | Scope |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `dashboards:write` | <ul><li>`dashboards:*`</li><li>`dashboards:uid:*`</li><li>`folders:*`</li><li>`folders:uid:*`</li></ul> |
|
||||
{ .no-spacing-list }
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
**Example Update Request**:
|
||||
|
||||
```http
|
||||
POST /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards/gdxccn HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"metadata": {
|
||||
"name": "gdxccn",
|
||||
"annotations": {
|
||||
"grafana.app/folder": "fef30w4jaxla8b",
|
||||
"grafana.app/message": "commit message"
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
"title": "New dashboard - updated",
|
||||
"schemaVersion": 41,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
JSON Body schema:
|
||||
|
||||
- **metadata.name** – The [unique identifier]({{< ref "#identifier-id-vs-unique-identifier-uid" >}}).
|
||||
- **metadata.annotations.grafana.app/folder** - Optional field, the unique identifier of the folder under which the dashboard should be created.
|
||||
- **metadata.annotations.grafana.app/message** - Optional field, to set a commit message for the version history.
|
||||
- **spec** – The dashboard json.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
Content-Length: 485
|
||||
|
||||
{
|
||||
"kind": "Dashboard",
|
||||
"apiVersion": "dashboard.grafana.app/v1beta1",
|
||||
"metadata": {
|
||||
"name": "gdxccn",
|
||||
"namespace": "default",
|
||||
"uid": "Cc7fA5ffHY94NnHZyMxXvFlpFtOmkK3qkBcVZPKSPXcX",
|
||||
"resourceVersion": "2",
|
||||
"generation": 2,
|
||||
"creationTimestamp": "2025-03-06T19:57:18Z",
|
||||
"annotations": {
|
||||
"grafana.app/folder": "fef30w4jaxla8b",
|
||||
"grafana.app/createdBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedTimestamp": "2025-03-07T02:58:36Z"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"schemaVersion": 41,
|
||||
"title": "New dashboard - updated",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** – OK
|
||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
||||
- **401** – Unauthorized
|
||||
- **403** – Access denied
|
||||
- **409** – Conflict (dashboard with the same version already exists)
|
||||
|
||||
## Get Dashboard
|
||||
|
||||
`GET /apis/dashboard.grafana.app/v1beta1/namespaces/:namespace/dashboards/:uid`
|
||||
|
||||
Gets a dashboard via the dashboard uid.
|
||||
|
||||
- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}).
|
||||
- uid: the unique identifier of the dashboard to update. this will be the _name_ in the dashboard response
|
||||
|
||||
Note: For large dashboards, add `/dto` to the end of the URL to get the full dashboard body.
|
||||
|
||||
**Required permissions**
|
||||
|
||||
See note in the [introduction]({{< ref "#dashboard-api" >}}) for an explanation.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
| Action | Scope |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `dashboards:read` | <ul><li>`dashboards:*`</li><li>`dashboards:uid:*`</li><li>`folders:*`</li><li>`folders:uid:*`</li></ul> |
|
||||
{ .no-spacing-list }
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
**Example Get Request**:
|
||||
|
||||
```http
|
||||
GET /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards/gdxccn HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
Content-Length: 485
|
||||
|
||||
{
|
||||
"kind": "Dashboard",
|
||||
"apiVersion": "dashboard.grafana.app/v1beta1",
|
||||
"metadata": {
|
||||
"name": "gdxccn",
|
||||
"namespace": "default",
|
||||
"uid": "Cc7fA5ffHY94NnHZyMxXvFlpFtOmkK3qkBcVZPKSPXcX",
|
||||
"resourceVersion": "2",
|
||||
"generation": 2,
|
||||
"creationTimestamp": "2025-03-06T19:57:18Z",
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedTimestamp": "2025-03-07T02:58:36Z"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"schemaVersion": 41,
|
||||
"title": "New dashboard - updated",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** – OK
|
||||
- **401** – Unauthorized
|
||||
- **403** – Access denied
|
||||
- **404** – Not Found
|
||||
|
||||
## List Dashboards
|
||||
|
||||
`GET /apis/dashboard.grafana.app/v1beta1/namespaces/:namespace/dashboards`
|
||||
|
||||
Lists all dashboards in the given organization. You can control the maximum number of dashboards returned through the `limit` query parameter. You can then use the `continue` token returned to fetch the next page of dashboards.
|
||||
|
||||
- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}).
|
||||
|
||||
Note: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}).
|
||||
|
||||
**Required permissions**
|
||||
|
||||
See note in the [introduction]({{< ref "#dashboard-api" >}}) for an explanation.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
| Action | Scope |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `dashboards:read` | <ul><li>`dashboards:*`</li><li>`dashboards:uid:*`</li><li>`folders:*`</li><li>`folders:uid:*`</li></ul> |
|
||||
{ .no-spacing-list }
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
**Example Get Request**:
|
||||
|
||||
```http
|
||||
GET /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards?limit=1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
Content-Length: 644
|
||||
|
||||
{
|
||||
"kind": "DashboardList",
|
||||
"apiVersion": "dashboard.grafana.app/v1alpha1",
|
||||
"metadata": {
|
||||
"resourceVersion": "1741315830000",
|
||||
"continue": "org:1/start:1158/folder:"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Dashboard",
|
||||
"apiVersion": "dashboard.grafana.app/v1alpha1",
|
||||
"metadata": {
|
||||
"name": "gpqcmf",
|
||||
"namespace": "default",
|
||||
"uid": "VQyL7pNTpfGPNlPM6HRJSePrBg5dXmxr4iPQL7txLtwX",
|
||||
"resourceVersion": "1",
|
||||
"generation": 1,
|
||||
"creationTimestamp": "2025-03-06T19:50:30Z",
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedTimestamp": "2025-03-06T19:50:30Z"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"schemaVersion": 41,
|
||||
"title": "New dashboard",
|
||||
"uid": "gpqcmf",
|
||||
"version": 1,
|
||||
...
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** – OK
|
||||
- **401** – Unauthorized
|
||||
- **403** – Access denied
|
||||
|
||||
## Delete Dashboard
|
||||
|
||||
`DELETE /apis/dashboard.grafana.app/v1beta1/namespaces/:namespace/dashboards/:uid`
|
||||
|
||||
Deletes a dashboard via the dashboard uid.
|
||||
|
||||
- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}).
|
||||
- uid: the unique identifier of the dashboard to update. this will be the _name_ in the dashboard response
|
||||
|
||||
**Required permissions**
|
||||
|
||||
See note in the [introduction]({{< ref "#dashboard-api" >}}) for an explanation.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
| Action | Scope |
|
||||
| ------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `dashboards:delete` | <ul><li>`dashboards:*`</li><li>`dashboards:uid:*`</li><li>`folders:*`</li><li>`folders:uid:*`</li></ul> |
|
||||
{ .no-spacing-list }
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
**Example Delete Request**:
|
||||
|
||||
```http
|
||||
DELETE /apis/dashboard.grafana.app/v1beta1/namespaces/default/dashboards/gdxccn HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
Content-Length: 78
|
||||
|
||||
{
|
||||
"kind": "Status",
|
||||
"apiVersion": "v1",
|
||||
"metadata": {},
|
||||
"status": "Success",
|
||||
"details": {
|
||||
"name": "gdxccn",
|
||||
"group": "dashboard.grafana.app",
|
||||
"kind": "dashboards",
|
||||
"uid": "Cc7fA5ffHY94NnHZyMxXvFlpFtOmkK3qkBcVZPKSPXcX"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** – OK
|
||||
- **401** – Unauthorized
|
||||
- **403** – Access denied
|
||||
- **404** – Not found
|
||||
|
||||
## Gets the home dashboard
|
||||
|
||||
`GET /api/dashboards/home`
|
||||
|
||||
Will return the home dashboard.
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/dashboards/home HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"dashboard": {
|
||||
"editable":false,
|
||||
"nav":[
|
||||
{
|
||||
"enable":false,
|
||||
"type":"timepicker"
|
||||
}
|
||||
],
|
||||
"style":"dark",
|
||||
"tags":[],
|
||||
"templating":{
|
||||
"list":[
|
||||
]
|
||||
},
|
||||
"time":{
|
||||
},
|
||||
"timezone":"browser",
|
||||
"title":"Home",
|
||||
"version":5
|
||||
},
|
||||
"meta": {
|
||||
"isHome":true,
|
||||
"canSave":false,
|
||||
"canEdit":false,
|
||||
"canStar":false,
|
||||
"url":"",
|
||||
"expires":"0001-01-01T00:00:00Z",
|
||||
"created":"0001-01-01T00:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Tags for Dashboard
|
||||
|
||||
`GET /api/dashboards/tags`
|
||||
|
||||
Get all tags of dashboards
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/dashboards/tags HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"term":"tag1",
|
||||
"count":1
|
||||
},
|
||||
{
|
||||
"term":"tag2",
|
||||
"count":4
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Dashboard Search
|
||||
|
||||
See [Folder/Dashboard Search API]({{< relref "folder_dashboard_search/" >}}).
|
||||
|
||||
## APIs
|
||||
|
||||
### Unique identifier (uid) vs identifier (id)
|
||||
|
||||
The unique identifier (uid) of a dashboard can be used to uniquely identify a dashboard within a given org.
|
||||
It's automatically generated if not provided when creating a dashboard. The uid allows having consistent URLs for accessing
|
||||
dashboards and when syncing dashboards between multiple Grafana installs, see [dashboard provisioning](/docs/grafana/latest/administration/provisioning/#dashboards)
|
||||
for more information. This means that changing the title of a dashboard will not break any bookmarked links to that dashboard.
|
||||
|
||||
The uid can have a maximum length of 40 characters.
|
||||
|
||||
## Create / Update dashboard
|
||||
The identifier (id) of a dashboard is deprecated in favor of the unique identifier (uid).
|
||||
|
||||
### Create / Update dashboard
|
||||
|
||||
`POST /api/dashboards/db`
|
||||
|
||||
|
@ -155,7 +992,7 @@ Content-Length: 97
|
|||
}
|
||||
```
|
||||
|
||||
## Get dashboard by uid
|
||||
### Get dashboard by uid
|
||||
|
||||
`GET /api/dashboards/uid/:uid`
|
||||
|
||||
|
@ -214,7 +1051,7 @@ Status Codes:
|
|||
- **403** – Access denied
|
||||
- **404** – Not found
|
||||
|
||||
## Delete dashboard by uid
|
||||
### Delete dashboard by uid
|
||||
|
||||
`DELETE /api/dashboards/uid/:uid`
|
||||
|
||||
|
|
|
@ -16,19 +16,388 @@ labels:
|
|||
title: Folder HTTP API
|
||||
---
|
||||
|
||||
# Folder API
|
||||
# New Folders APIs
|
||||
|
||||
> If you are running Grafana Enterprise, for some endpoints you'll need to have specific permissions. Refer to [Role-based access control permissions](/docs/grafana/latest/administration/roles-and-permissions/access-control/custom-role-actions-scopes/) for more information.
|
||||
|
||||
> To view more about the new api structure, refer to [API overview]({{< ref "apis" >}}).
|
||||
|
||||
### Get all folders
|
||||
|
||||
`GET /apis/folder.grafana.app/v1beta1/namespaces/:namespace/folders`
|
||||
|
||||
Returns all folders that the authenticated user has permission to view within the given organization. Use the `limit` query parameter to control the maximum number of dashboards returned. To retrieve additional dashboards, utilize the `continue` token provided in the response to fetch the next page.
|
||||
|
||||
- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}).
|
||||
|
||||
**Required permissions**
|
||||
|
||||
See note in the [introduction]({{< ref "#folder-api" >}}) for an explanation.
|
||||
|
||||
| Action | Scope |
|
||||
| -------------- | ----------- |
|
||||
| `folders:read` | `folders:*` |
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /apis/folder.grafana.app/v1beta1/namespaces/default/folders?limit=1 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"kind": "FolderList",
|
||||
"apiVersion": "folder.grafana.app/v1beta1",
|
||||
"metadata": {
|
||||
"continue": "org:1/start:1158/folder:"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"kind": "Folder",
|
||||
"apiVersion": "folder.grafana.app/v1beta1",
|
||||
"metadata": {
|
||||
"name": "aef30vrzxs3y8d",
|
||||
"namespace": "default",
|
||||
"uid": "KCtv1FXDsJmTYQoTgcPnfuwZhDZge3uMpXOefaOHjb4X",
|
||||
"resourceVersion": "1741343686000",
|
||||
"creationTimestamp": "2025-03-07T10:34:46Z",
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedTimestamp": "2025-03-07T10:34:46Z"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"title": "example"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** – OK
|
||||
- **401** – Unauthorized
|
||||
- **403** – Access Denied
|
||||
|
||||
### Get folder by uid
|
||||
|
||||
`GET /apis/folder.grafana.app/v1beta1/namespaces/:namespace/folders/:uid`
|
||||
|
||||
Will return the folder given the folder uid.
|
||||
|
||||
- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}).
|
||||
- uid: the unique identifier of the folder to update. this will be the _name_ in the folder response
|
||||
|
||||
**Required permissions**
|
||||
|
||||
See note in the [introduction]({{< ref "#folder-api" >}}) for an explanation.
|
||||
|
||||
| Action | Scope |
|
||||
| -------------- | ----------- |
|
||||
| `folders:read` | `folders:*` |
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /apis/folder.grafana.app/v1beta1/namespaces/default/folders/aef30vrzxs3y8d HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"kind": "Folder",
|
||||
"apiVersion": "folder.grafana.app/v1beta1",
|
||||
"metadata": {
|
||||
"name": "aef30vrzxs3y8d",
|
||||
"namespace": "default",
|
||||
"uid": "KCtv1FXDsJmTYQoTgcPnfuwZhDZge3uMpXOefaOHjb4X",
|
||||
"resourceVersion": "1741343686000",
|
||||
"creationTimestamp": "2025-03-07T10:34:46Z",
|
||||
"annotations": {
|
||||
"grafana.app/createdBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedTimestamp": "2025-03-07T10:34:46Z",
|
||||
"grafana.app/folder": "fef30w4jaxla8b"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"title": "test"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note the annotation `grafana.app/folder` which contains the uid of the parent folder.
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** – Found
|
||||
- **401** – Unauthorized
|
||||
- **403** – Access Denied
|
||||
- **404** – Folder not found
|
||||
|
||||
### Create folder
|
||||
|
||||
`POST /apis/folder.grafana.app/v1beta1/namespaces/:namespace/folders`
|
||||
|
||||
Creates a new folder.
|
||||
|
||||
- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}).
|
||||
|
||||
**Required permissions**
|
||||
|
||||
See note in the [introduction]({{< ref "#folder-api" >}}) for an explanation.
|
||||
|
||||
`folders:create` allows creating folders and subfolders. If granted with scope `folders:uid:general`, allows creating root level folders. Otherwise, allows creating subfolders under the specified folders.
|
||||
|
||||
| Action | Scope |
|
||||
| ---------------- | ----------- |
|
||||
| `folders:create` | `folders:*` |
|
||||
| `folders:write` | `folders:*` |
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
POST /apis/folder.grafana.app/v1beta1/namespaces/default/folders HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
{
|
||||
"metadata": {
|
||||
"name": "aef30vrzxs3y8d",
|
||||
"annotations": {
|
||||
"grafana.app/folder": "fef30w4jaxla8b"
|
||||
}
|
||||
}
|
||||
"spec": {
|
||||
"title": "child-folder"
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
JSON Body schema:
|
||||
|
||||
- **metadata.name** – The Grafana [unique identifier]({{< ref "#identifier-id-vs-unique-identifier-uid" >}}). If you do not want to provide this, set metadata.generateName to the prefix you would like for the uid.
|
||||
- **metadata.annotations.grafana.app/folder** - Optional field, the unique identifier of the parent folder under which the folder should be created. Requires nested folders to be enabled.
|
||||
- **spec.title** – The title of the folder.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"kind": "Folder",
|
||||
"apiVersion": "folder.grafana.app/v1beta1",
|
||||
"metadata": {
|
||||
"name": "eef33r1fprd34d",
|
||||
"namespace": "default",
|
||||
"uid": "X8momvVZnsXdOqvLD9I4ngqLVif2CgRWXHy9xb2UgjQX",
|
||||
"resourceVersion": "1741320415009",
|
||||
"creationTimestamp": "2025-03-07T04:06:55Z",
|
||||
"labels": {
|
||||
"grafana.app/deprecatedInternalID": "1159"
|
||||
},
|
||||
"annotations": {
|
||||
"grafana.app/folder": "fef30w4jaxla8b",
|
||||
"grafana.app/createdBy": "service-account:cef2t2rfm73lsb"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"title": "child-folder"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **201** – Created
|
||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
||||
- **401** – Unauthorized
|
||||
- **403** – Access denied
|
||||
- **409** – Conflict (folder with the same uid already exists)
|
||||
|
||||
### Update folder
|
||||
|
||||
`PUT /apis/folder.grafana.app/v1beta1/namespaces/:namespace/folders/:uid`
|
||||
|
||||
Updates an existing folder identified by uid.
|
||||
|
||||
- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}).
|
||||
- uid: the unique identifier of the folder to update. this will be the _name_ in the folder response
|
||||
|
||||
**Required permissions**
|
||||
|
||||
See note in the [introduction]({{< ref "#folder-api" >}}) for an explanation.
|
||||
|
||||
| Action | Scope |
|
||||
| --------------- | ----------- |
|
||||
| `folders:write` | `folders:*` |
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
PUT /apis/folder.grafana.app/v1beta1/namespaces/default/folders/fef30w4jaxla8b HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
"metadata": {
|
||||
"name": "aef30vrzxs3y8d",
|
||||
"annotations": {
|
||||
"grafana.app/folder": "xkj92m5pqw3vn4"
|
||||
}
|
||||
}
|
||||
"spec": {
|
||||
"title": "updated title"
|
||||
},
|
||||
```
|
||||
|
||||
JSON Body schema:
|
||||
|
||||
- **metadata.name** – The [unique identifier]({{< ref "#identifier-id-vs-unique-identifier-uid" >}}) of the folder.
|
||||
- **metadata.annotations.grafana.app/folder** - Optional field, the unique identifier of the parent folder under which the folder should be - update this to move the folder under a different parent folder. Requires nested folders to be enabled.
|
||||
- **spec.title** – The title of the folder.
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"kind": "Folder",
|
||||
"apiVersion": "folder.grafana.app/v1beta1",
|
||||
"metadata": {
|
||||
"name": "fef30w4jaxla8b",
|
||||
"namespace": "default",
|
||||
"uid": "YaWLsFrMwEaTlIQwX2iMnhHlJuZHtZugps50BQoyjXEX",
|
||||
"resourceVersion": "1741345736000",
|
||||
"creationTimestamp": "2025-03-07T11:08:56Z",
|
||||
"annotations": {
|
||||
"grafana.app/folder": "xkj92m5pqw3vn4",
|
||||
"grafana.app/createdBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedTimestamp": "2025-03-07T11:08:56Z"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"title": "updated title"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** – Updated
|
||||
- **400** – Errors (invalid json, missing or invalid fields, etc)
|
||||
- **401** – Unauthorized
|
||||
- **403** – Access Denied
|
||||
- **404** – Folder not found
|
||||
- **412** – Precondition failed (the folder has been changed by someone else). With this status code, the response body will have the following properties:
|
||||
|
||||
```http
|
||||
HTTP/1.1 412 Precondition Failed
|
||||
Content-Type: application/json; charset=UTF-8
|
||||
Content-Length: 97
|
||||
|
||||
{
|
||||
"message": "The folder has been changed by someone else",
|
||||
"status": "version-mismatch"
|
||||
}
|
||||
```
|
||||
|
||||
### Delete folder
|
||||
|
||||
`DELETE /apis/folder.grafana.app/v1beta1/namespaces/:namespace/folders/:uid`
|
||||
|
||||
Deletes an existing folder identified by UID along with all dashboards (and their alerts) stored in the folder. This operation cannot be reverted.
|
||||
|
||||
If [Grafana Alerting]({{< relref "/docs/grafana/latest/alerting" >}}) is enabled, you can set an optional query parameter `forceDeleteRules=false` so that requests will fail with 400 (Bad Request) error if the folder contains any Grafana alerts. However, if this parameter is set to `true` then it will delete any Grafana alerts under this folder.
|
||||
|
||||
- namespace: to read more about the namespace to use, see the [API overview]({{< ref "apis" >}}).
|
||||
- uid: the unique identifier of the folder to delete. this will be the _name_ in the folder response
|
||||
|
||||
**Required permissions**
|
||||
|
||||
See note in the [introduction]({{< ref "#folder-api" >}}) for an explanation.
|
||||
|
||||
| Action | Scope |
|
||||
| ---------------- | ----------- |
|
||||
| `folders:delete` | `folders:*` |
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
DELETE /apis/folder.grafana.app/v1beta1/namespaces/default/folders/fef30w4jaxla8b HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"kind": "Folder",
|
||||
"apiVersion": "folder.grafana.app/v1beta1",
|
||||
"metadata": {
|
||||
"name": "fef30w4jaxla8b",
|
||||
"namespace": "default",
|
||||
"uid": "YaWLsFrMwEaTlIQwX2iMnhHlJuZHtZugps50BQoyjXEX",
|
||||
"resourceVersion": "1741345736000",
|
||||
"creationTimestamp": "2025-03-07T11:08:56Z",
|
||||
"annotations": {
|
||||
"grafana.app/folder": "xkj92m5pqw3vn4",
|
||||
"grafana.app/createdBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedBy": "service-account:cef2t2rfm73lsb",
|
||||
"grafana.app/updatedTimestamp": "2025-03-07T11:08:56Z"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"title": "updated title"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** – Deleted
|
||||
- **401** – Unauthorized
|
||||
- **400** – Bad Request
|
||||
- **403** – Access Denied
|
||||
- **404** – Folder not found
|
||||
|
||||
## APIs
|
||||
|
||||
## Identifier (id) vs unique identifier (uid)
|
||||
|
||||
The identifier (id) of a folder is an auto-incrementing numeric value and is only unique per Grafana install.
|
||||
|
||||
The unique identifier (uid) of a folder can be used for uniquely identify folders between multiple Grafana installs. It's automatically generated if not provided when creating a folder. The uid allows having consistent URLs for accessing folders and when syncing folders between multiple Grafana installs. This means that changing the title of a folder will not break any bookmarked links to that folder.
|
||||
The unique identifier (uid) of a folder can be used for uniquely identify folders within an org. It's automatically generated if not provided when creating a folder. The uid allows having consistent URLs for accessing folders and when syncing folders between multiple Grafana installs. This means that changing the title of a folder will not break any bookmarked links to that folder.
|
||||
|
||||
The uid can have a maximum length of 40 characters.
|
||||
|
||||
## Get all folders
|
||||
The identifier (id) of a folder is deprecated in favor of the unique identifier (uid).
|
||||
|
||||
### Get all folders
|
||||
|
||||
`GET /api/folders`
|
||||
|
||||
|
@ -75,7 +444,7 @@ Content-Type: application/json
|
|||
]
|
||||
```
|
||||
|
||||
## Get folder by uid
|
||||
### Get folder by uid
|
||||
|
||||
`GET /api/folders/:uid`
|
||||
|
||||
|
@ -133,7 +502,7 @@ Status Codes:
|
|||
- **403** – Access Denied
|
||||
- **404** – Folder not found
|
||||
|
||||
## Create folder
|
||||
### Create folder
|
||||
|
||||
`POST /api/folders`
|
||||
|
||||
|
@ -207,7 +576,7 @@ Status Codes:
|
|||
- **403** – Access Denied
|
||||
- **412** - Folder already exists
|
||||
|
||||
## Update folder
|
||||
### Update folder
|
||||
|
||||
`PUT /api/folders/:uid`
|
||||
|
||||
|
@ -296,7 +665,7 @@ Content-Length: 97
|
|||
}
|
||||
```
|
||||
|
||||
## Delete folder
|
||||
### Delete folder
|
||||
|
||||
`DELETE /api/folders/:uid`
|
||||
|
||||
|
@ -342,7 +711,7 @@ Status Codes:
|
|||
- **403** – Access Denied
|
||||
- **404** – Folder not found
|
||||
|
||||
## Move folder
|
||||
### Move folder
|
||||
|
||||
`POST /api/folders/:uid/move`
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
_build:
|
||||
list: false
|
||||
noindex: true
|
||||
cascade:
|
||||
noindex: true
|
||||
description: Overview of Grafana CLI, a command line tool for managing Grafana resources as code.
|
||||
keywords:
|
||||
- observability
|
||||
- configuration
|
||||
- as code
|
||||
- as-code
|
||||
- dashboards
|
||||
- git integration
|
||||
- git sync
|
||||
- github
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
cards:
|
||||
items:
|
||||
- description: Learn how to install Grafana CLI
|
||||
height: 24
|
||||
href: ./install-grafana-cli/
|
||||
title: Install Grafana CLI
|
||||
- description: Set up Grafana CLI
|
||||
height: 24
|
||||
href: ./set-up-grafana-cli/
|
||||
title: Set up your Grafana CLI
|
||||
- description: Learn how to manage resources with Grafana CLI
|
||||
height: 24
|
||||
href: ./grafanacli-workflows
|
||||
title: Manage resources with Grafana CLI
|
||||
title_class: pt-0 lh-1
|
||||
hero:
|
||||
description: Grafana CLI (`grafanactl`) is a command-line tool designed to simplify interaction with Grafana instances. It enables users to authenticate, manage multiple environments, and perform administrative tasks through Grafana’s REST API, all from the terminal. Whether you're automating workflows in CI/CD pipelines or switching between staging and production environments, Grafana CLI provides a flexible and scriptable way to manage your Grafana setup efficiently.
|
||||
height: 110
|
||||
level: 1
|
||||
title: Grafana CLI
|
||||
width: 110
|
||||
title: Introduction to Grafana CLI
|
||||
menuTitle: Grafana CLI
|
||||
weight: 130
|
||||
---
|
||||
|
||||
{{< docs/hero-simple key="hero" >}}
|
||||
|
||||
## Explore
|
||||
|
||||
{{< card-grid key="cards" type="simple" >}}
|
|
@ -0,0 +1,226 @@
|
|||
---
|
||||
_build:
|
||||
list: false
|
||||
noindex: true
|
||||
cascade:
|
||||
noindex: true
|
||||
description: Learn more about the supported workflows and use cases for Grafana CLI
|
||||
keywords:
|
||||
- workflows
|
||||
- Grafana CLI
|
||||
- CLI
|
||||
- command line
|
||||
- grafanactl
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
title: Manage resources with Grafana CLI
|
||||
weight: 300
|
||||
---
|
||||
|
||||
# Manage resources with Grafana CLI
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
`grafanactl` is under active development. Command-line flags and subcommands described here may change. This document outlines the target workflows the tool is expected to support.
|
||||
{{< /admonition >}}
|
||||
|
||||
## Migrate resources between environments
|
||||
|
||||
Using the `config` and `resources` options, you can migrate Grafana resources from one environment to another, for example, from a development to production environment.
|
||||
The `config` option lets you define the configuration context.
|
||||
Using `resources` with `pull`, `push`, and `serve` lets you pull a defined resource from one instance, and push that resource to another instance. `Serve` allows you to preview changes locally before pushing.
|
||||
|
||||
Use these steps to migrate resources between environments:
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
Currently, the `serve` command only works with dashboards.
|
||||
{{< /admonition >}}
|
||||
|
||||
Use these steps to migrate resources between environments:
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
Resources are pulled and pushed from the `./resources` directory by default.
|
||||
This directory can be configured with the `--directory`/`-d` flags.
|
||||
{{< /admonition >}}
|
||||
|
||||
1. Make changes to dashboards and other resources using the Grafana UI in your **development instance**.
|
||||
1. Pull those resources from the development environment to your local machine:
|
||||
|
||||
```bash
|
||||
grafanactl config use-context YOUR_CONTEXT # for example "dev"
|
||||
grafanactl resources pull -d ./resources/ -o yaml # or json
|
||||
```
|
||||
|
||||
1. (Optional) Preview the resources locally before pushing:
|
||||
|
||||
```bash
|
||||
grafanactl config use-context YOUR_CONTEXT # for example "prod"
|
||||
grafanactl resources serve -d ./resources/
|
||||
```
|
||||
|
||||
1. Switch to the **production instance** and push the resources:
|
||||
|
||||
```bash
|
||||
grafanactl config use-context YOUR_CONTEXT # for example "prod"
|
||||
grafanactl resources push -d ./resources/
|
||||
```
|
||||
|
||||
## Back up Grafana resources
|
||||
|
||||
This workflow helps you back up all Grafana resources from one instance and later restore them. This is useful to replicate a configuration or perform disaster recovery.
|
||||
|
||||
1. Use `grafanactl` to pull all resources from your target environment:
|
||||
|
||||
```bash
|
||||
grafanactl config use-context YOUR_CONTEXT # for example "prod"
|
||||
grafanactl resources pull -d ./resources/ -o yaml # or json
|
||||
```
|
||||
|
||||
1. Save the exported resources to version control or cloud storage.
|
||||
|
||||
## Restore Grafana resources
|
||||
|
||||
1. (Optional) Preview the backup locally:
|
||||
|
||||
```bash
|
||||
grafanactl config use-context YOUR_CONTEXT # for example "prod"
|
||||
grafanactl resources serve -d ./resources/
|
||||
```
|
||||
|
||||
1. To restore the resources later or restore them on another instance, push the saved resources:
|
||||
|
||||
```bash
|
||||
grafanactl config use-context YOUR_CONTEXT # for example "prod"
|
||||
grafanactl resources push -d ./resources/
|
||||
```
|
||||
|
||||
## Manage dashboards as code
|
||||
|
||||
With this workflow, you can define and manage dashboards as code, saving them to a version control system like Git. This is useful for teams that want to maintain a history of changes, collaborate on dashboard design, and ensure consistency across environments.
|
||||
|
||||
1. Use a dashboard generation script (for example, with the [Foundation SDK](https://github.com/grafana/grafana-foundation-sdk)). You can find an example implementation in the Grafana as code [hands-on lab repository](https://github.com/grafana/dashboards-as-code-workshop/tree/main/part-one-golang).
|
||||
|
||||
1. Serve and preview the output of the dashboard generator locally:
|
||||
|
||||
```bash
|
||||
grafanactl config use-context YOUR_CONTEXT # for example "dev"
|
||||
grafanactl resources serve --script 'go run scripts/generate-dashboard.go' --watch './scripts'
|
||||
```
|
||||
|
||||
1. When the output looks correct, generate dashboard manifest files:
|
||||
|
||||
```bash
|
||||
go run scripts/generate-dashboard.go --generate-resource-manifests --output './resources'
|
||||
```
|
||||
|
||||
1. Push the generated resources to your Grafana instance:
|
||||
|
||||
```bash
|
||||
grafanactl config use-context YOUR_CONTEXT # for example "dev"
|
||||
grafanactl resources push -d ./resources/
|
||||
```
|
||||
|
||||
## Explore and modify resources from the terminal
|
||||
|
||||
This section describes how to use the Grafana CLI to interact with Grafana resources directly from your terminal. These commands allow you to browse, inspect, update, and delete resources without using the Grafana UI. This approach is useful for advanced users who want to manage resources more efficiently or integrate Grafana operations into automated workflows.
|
||||
|
||||
### Find and delete dashboards using invalid data sources
|
||||
|
||||
Use this workflow to identify dashboards that reference incorrect or outdated data sources, and remove them if necessary.
|
||||
|
||||
1. Set the context to the appropriate environment:
|
||||
|
||||
```bash
|
||||
grafanactl config use-context YOUR_CONTEXT # for example "prod"
|
||||
```
|
||||
|
||||
1. Find dashboards using specific data sources:
|
||||
|
||||
```bash
|
||||
grafanactl resources get dashboards -ojson | jq '.items | map({ uid: .metadata.name, datasources: .spec.panels | map(.datasource.uid) })'
|
||||
[
|
||||
{
|
||||
"uid": "important-production-dashboard",
|
||||
"datasources": [
|
||||
"mimir-prod"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "test-dashboard-from-dev",
|
||||
"datasources": [
|
||||
"mimir-prod",
|
||||
"mimir-dev"
|
||||
]
|
||||
},
|
||||
{
|
||||
"uid": "test-dashboard-from-stg",
|
||||
"datasources": [
|
||||
"mimir-prod",
|
||||
"mimir-stg",
|
||||
"mimir-dev"
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
This command lists dashboard UIDs along with the data source UIDs used in their panels. You can then identify the dashboards that are using invalid or unexpected data sources.
|
||||
|
||||
1. Delete the identified dashboards directly:
|
||||
|
||||
```bash
|
||||
grafanactl resources delete dashboards/test-dashboard-from-stg,test-dashboard-from-dev
|
||||
✔ 2 resources deleted, 0 errors
|
||||
```
|
||||
|
||||
### Find and deprecate dashboards using the old API version
|
||||
|
||||
Use this workflow to locate dashboards using a deprecated API version and mark them accordingly.
|
||||
|
||||
1. Set the context to the appropriate environment:
|
||||
|
||||
```bash
|
||||
grafanactl config use-context YOUR_CONTEXT # for example "prod"
|
||||
```
|
||||
|
||||
1. List all available resources types and versions:
|
||||
|
||||
```bash
|
||||
grafanactl resources list
|
||||
```
|
||||
|
||||
This command returns a list of resources, including their versions, types, and quantities:
|
||||
|
||||
```bash
|
||||
GROUP VERSION KIND
|
||||
folder.grafana.app v1 folder
|
||||
dashboard.grafana.app v1 dashboard
|
||||
dashboard.grafana.app v1 librarypanel
|
||||
dashboard.grafana.app v2 dashboard
|
||||
dashboard.grafana.app v2 librarypanel
|
||||
playlist.grafana.app v1 playlist
|
||||
```
|
||||
|
||||
1. Find dashboards that are still using an old API version:
|
||||
|
||||
```bash
|
||||
grafanactl resources get dashboards.v1.dashboard.grafana.app
|
||||
```
|
||||
|
||||
This command returns a table displaying the resource type, resource name, and associated namespace:
|
||||
|
||||
```bash
|
||||
KIND NAME NAMESPACE
|
||||
dashboards really-old-dashboard default
|
||||
```
|
||||
|
||||
1. Edit each of these dashboards to add a `deprecated` tag:
|
||||
|
||||
```bash
|
||||
grafanactl resources edit dashboards.v1.dashboard.grafana.app/really-old-dashboard -p '{"spec":{"tags":["deprecated"]}}'
|
||||
```
|
||||
|
||||
{{< admonition type="tip" >}}
|
||||
You can get help by using the `grafanactl --help` command.
|
||||
{{< /admonition >}}
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
_build:
|
||||
list: false
|
||||
noindex: true
|
||||
cascade:
|
||||
noindex: true
|
||||
description: Installation guide for Grafana CLI, a command line tool for managing Grafana Observability as Code
|
||||
keywords:
|
||||
- configuration
|
||||
- Grafana CLI
|
||||
- CLI
|
||||
- command line
|
||||
- grafanactl
|
||||
- installation
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
title: Install Grafana CLI
|
||||
weight: 100
|
||||
---
|
||||
|
||||
# Install Grafana CLI
|
||||
|
||||
You can install the project using one of the following supported methods:
|
||||
|
||||
## 1. Download a pre-built binary
|
||||
|
||||
Download the latest binary for your platform from the [Releases page](https://github.com/grafana/grafanactl/releases).
|
||||
|
||||
Prebuilt binaries are available for a variety of operating systems and architectures. Visit the latest release page, and scroll down to the Assets section.
|
||||
|
||||
To install the binary, follow the instructions below:
|
||||
|
||||
1. Download the archive for the desired operating system and architecture
|
||||
1. Extract the archive
|
||||
1. Move the executable to the desired directory
|
||||
1. Ensure this directory is included in the PATH environment variable
|
||||
1. Verify that you have execute permission on the file
|
||||
|
||||
## 2. Build from source
|
||||
|
||||
To build `grafanactl` from source you must:
|
||||
|
||||
- Have `git` installed
|
||||
- Have `go` v1.24 (or greater) installed
|
||||
|
||||
```bash
|
||||
go install github.com/grafana/grafanactl/cmd@latest
|
||||
```
|
|
@ -0,0 +1,122 @@
|
|||
---
|
||||
_build:
|
||||
list: false
|
||||
noindex: true
|
||||
cascade:
|
||||
noindex: true
|
||||
description: Configuration guide for Grafana CLI, a command line tool for managing Grafana resources as code.
|
||||
keywords:
|
||||
- configuration
|
||||
- Grafana CLI
|
||||
- CLI
|
||||
- command line
|
||||
- grafanactl
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
title: Set up Grafana CLI
|
||||
weight: 200
|
||||
---
|
||||
|
||||
# Set up Grafana CLI
|
||||
|
||||
You can configure Grafana CLI in two ways: using environment variables or through a configuration file.
|
||||
|
||||
- **Environment variables** are ideal for CI environments and support a single context.
|
||||
- **Configuration files** can manage multiple contexts, making it easier to switch between different Grafana instances.
|
||||
|
||||
## Use environment variables
|
||||
|
||||
Grafana CLI communicates with Grafana via its REST API, which requires authentication credentials.
|
||||
|
||||
At a minimum, set the URL of your Grafana instance and the organization ID:
|
||||
|
||||
```bash
|
||||
GRAFANA_SERVER='http://localhost:3000' GRAFANA_ORG_ID='1' grafanactl config check
|
||||
```
|
||||
|
||||
Depending on your authentication method, you may also need to set:
|
||||
|
||||
- A [token](https://github.com/grafana/grafanactl/blob/main/docs/reference/environment-variables/index.md#grafana_token) for a [Grafana service account](https://grafana.com/docs/grafana/latest/administration/service-accounts/) (recommended)
|
||||
- A [username](https://github.com/grafana/grafanactl/blob/main/docs/reference/environment-variables/index.md#grafana_user) and [password](https://github.com/grafana/grafanactl/blob/main/docs/reference/environment-variables/index.md#grafana_password) for basic authentication
|
||||
|
||||
To persist your configuration, consider [creating a context](#defining-contexts).
|
||||
|
||||
A full list of supported environment variables is available in the [reference documentation](https://github.com/grafana/grafanactl/blob/main/docs/reference/environment-variables/index.md#environment-variables-reference).
|
||||
|
||||
## Define contexts
|
||||
|
||||
Contexts allow you to easily switch between multiple Grafana instances. By default, the CLI uses a context named `default`.
|
||||
|
||||
To configure the `default` context:
|
||||
|
||||
```bash
|
||||
grafanactl config set contexts.default.grafana.server http://localhost:3000
|
||||
grafanactl config set contexts.default.grafana.org-id 1
|
||||
|
||||
# Authenticate with a service account token
|
||||
grafanactl config set contexts.default.grafana.token service-account-token
|
||||
|
||||
# Or use basic authentication
|
||||
grafanactl config set contexts.default.grafana.user admin
|
||||
grafanactl config set contexts.default.grafana.password admin
|
||||
```
|
||||
|
||||
You can define additional contexts in the same way:
|
||||
|
||||
```bash
|
||||
grafanactl config set contexts.staging.grafana.server https://staging.grafana.example
|
||||
grafanactl config set contexts.staging.grafana.org-id 1
|
||||
```
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
In these examples, `default` and `staging` are the names of the contexts.
|
||||
{{< /admonition >}}
|
||||
|
||||
## Configuration file
|
||||
|
||||
Grafana CLI stores its configuration in a YAML file. The CLI determines the configuration file location in the following order:
|
||||
|
||||
1. If the `--config` flag is provided, the specified file is used.
|
||||
2. If `$XDG_CONFIG_HOME` is set:
|
||||
`$XDG_CONFIG_HOME/grafanactl/config.yaml`
|
||||
3. If `$HOME` is set:
|
||||
`$HOME/.config/grafanactl/config.yaml`
|
||||
4. If `$XDG_CONFIG_DIRS` is set:
|
||||
`$XDG_CONFIG_DIRS/grafanactl/config.yaml`
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
Use `grafanactl config check` to display the configuration file currently in use.
|
||||
{{< /admonition >}}
|
||||
|
||||
## Useful commands
|
||||
|
||||
Check the current configuration:
|
||||
|
||||
```bash
|
||||
grafanactl config check
|
||||
```
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
This command is useful to troubleshoot your configuration.
|
||||
{{< /admonition >}}
|
||||
|
||||
List all available contexts:
|
||||
|
||||
```bash
|
||||
grafanactl config list-contexts
|
||||
```
|
||||
|
||||
Switch to a specific context:
|
||||
|
||||
```bash
|
||||
grafanactl config use-context staging
|
||||
```
|
||||
|
||||
View the full configuration:
|
||||
|
||||
```bash
|
||||
grafanactl config view
|
||||
```
|
|
@ -855,6 +855,23 @@ Path to the default home dashboard. If this value is empty, then Grafana uses St
|
|||
On Linux, Grafana uses `/usr/share/grafana/public/dashboards/home.json` as the default home dashboard location.
|
||||
{{< /admonition >}}
|
||||
|
||||
### `[dashboard_cleanup]`
|
||||
|
||||
Settings related to cleaning up associated dashboards information if the dashboard was deleted through /apis.
|
||||
|
||||
#### `interval`
|
||||
|
||||
How often to run the job to cleanup associated resources. The default interval is `30s`. The minimum allowed value is `10s` to ensure the system isn't overloaded.
|
||||
|
||||
The interval string must include a unit suffix (ms, s, m, h), e.g. 30s or 1m.
|
||||
|
||||
#### `batch_size`
|
||||
|
||||
Number of deleted dashboards to process in each batch during the cleanup process.
|
||||
Default: `10`, Minimum: `5`, Maximum: `200`.
|
||||
|
||||
Increasing this value allows processing more dashboards in each cleanup cycle but may impact system performance.
|
||||
|
||||
<hr />
|
||||
|
||||
### `[datasources]`
|
||||
|
|
|
@ -46,7 +46,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
|
|||
| `panelMonitoring` | Enables panel monitoring through logs and measurements | Yes |
|
||||
| `formatString` | Enable format string transformer | Yes |
|
||||
| `kubernetesClientDashboardsFolders` | Route the folder and dashboard service requests to k8s | Yes |
|
||||
| `recoveryThreshold` | Enables feature recovery threshold (aka hysteresis) for threshold server-side expression | Yes |
|
||||
| `lokiStructuredMetadata` | Enables the loki data source to request structured metadata from the Loki server | Yes |
|
||||
| `addFieldFromCalculationStatFunctions` | Add cumulative and window functions to the add field from calculation transformation | Yes |
|
||||
| `annotationPermissionUpdate` | Change the way annotation permissions work by scoping them to folders and dashboards. | Yes |
|
||||
|
|
|
@ -46,22 +46,30 @@ We provide release documentation in multiple places to address different needs:
|
|||
|
||||
## When to expect releases
|
||||
|
||||
Currently, Grafana is on a monthly release cycle. Here’s a look at scheduled releases for the first half of 2025:
|
||||
Currently, Grafana is on a monthly release cycle. Here’s a look at scheduled releases for 2025:
|
||||
|
||||
| **Anticipated release date** | **Grafana versions** | **Release type** |
|
||||
| ---------------------------- | --------------------------------- | ---------------- |
|
||||
| Jan. 28, 2025 | 11.5 & Supported versions | Minor & patching |
|
||||
| Feb. 18, 2025 | Supported versions | Patching |
|
||||
| March 25, 2025 | 11.6 & Supported versions | Minor & patching |
|
||||
| April 15, 2025 | Supported versions | Patching |
|
||||
| May 5, 2025 | Grafana 12.0 & Supported versions | Major & patching |
|
||||
| **Release date** | **Grafana versions** | **Release type** |
|
||||
| ---------------- | ------------------------- | ---------------- |
|
||||
| Jan. 28, 2025 | 11.5 & Supported versions | Minor & patching |
|
||||
| Feb. 18, 2025 | Supported versions | Patching |
|
||||
| March 25, 2025 | 11.6 & Supported versions | Minor & patching |
|
||||
| April 23, 2025 | Supported versions | Patching |
|
||||
| May 5, 2025 | Grafana 12.0 | Major only |
|
||||
| May 20, 2025 | Supported versions | Patching |
|
||||
| June 17, 2025 | Supported versions | Patching |
|
||||
| July 22, 2025 | 12.1 & Supported versions | Minor & patching |
|
||||
| Aug. 12, 2025 | Supported versions | Patching |
|
||||
| Sept. 23, 2025 | 12.2 & Supported versions | Minor & patching |
|
||||
| Oct. 21, 2025 | Supported versions | Patching |
|
||||
| Nov. 18, 2025 | 12.3 & Supported versions | Minor & patching |
|
||||
| Dec. 16, 2025 | Supported versions | Patching |
|
||||
|
||||
### A few important notes
|
||||
|
||||
- The schedule above outlines how we plan release dates. However, unforeseen events and circumstances may cause dates to change.
|
||||
- High severity security and feature degradation incidents will result in ad-hoc releases that are not scheduled ahead of time.
|
||||
- Patching releases are for the current (last released) minor version of Grafana. Additional older versions of Grafana may be included if there is a critical bug or security vulnerability that needs to be patched.
|
||||
- Release freezes: Each year Grafana implements two release freezes to accommodate for the holiday season, these dates will be announced as the holiday season approaches. During these times, no scheduled releases will be executed. However, this does not apply to changes that may be required during the course of an operational or security incident.
|
||||
- Release freezes: Each year Grafana implements two release freezes to accommodate for the holiday season. During these times, no scheduled releases will be executed. However, this does not apply to changes that may be required during the course of an operational or security incident.
|
||||
|
||||
## Grafana security releases: improved version naming convention
|
||||
|
||||
|
@ -81,34 +89,57 @@ This naming convention should make it easier to identify security updates and th
|
|||
|
||||
## What to know about version support
|
||||
|
||||
Self-managed Grafana users have control over when they upgrade to a new version of Grafana. To help you make an informed decision about whether it's time to upgrade, it’s important that you understand the level of support provided for your current version.
|
||||
Self-managed Grafana users have control over when they upgrade to a new version of Grafana. To help you make an informed decision about whether it’s time to upgrade, it’s important that you understand the level of support provided for your current version.
|
||||
|
||||
For self-managed Grafana (both Enterprise and OSS), the support for versions is as follows:
|
||||
For self-managed Grafana (both Enterprise and OSS), the support for versions follows these rules:
|
||||
|
||||
- Support for each minor release extends to nine months after the release date.
|
||||
- Support for the last minor release of a major version is extended an additional six months, for a total of 15 months of support after the release date.
|
||||
- Each minor release is supported for 9 months after its release date
|
||||
- The last minor release of a major version receives extended support for 15 months after its release date
|
||||
- Support levels change as new versions are released:
|
||||
- **Full Support**: The most recently released major/minor (and the last minor of the previous major) version receive full support including new features, bug fixes, and security patches
|
||||
- **Security & Critical Bugs Only**: Versions that are not the most recently released major/minor (or the last minor of the previous major) version, but still within their support period, receive only security patches and critical bug fixes
|
||||
- **Not Supported**: Versions beyond their support period receive no updates
|
||||
|
||||
Here is an overview of projected version support through 2025:
|
||||
Here is an overview of version support through 2026:
|
||||
|
||||
| **Version** | **Release date** | **Support end of life (EOL)** |
|
||||
| ----------------------- | ---------------- | --------------------------------------- |
|
||||
| 10.4 (Last minor of 10) | March 2024 | June 2025 (extended support) |
|
||||
| 11.0 | May 2024 | NO LONGER SUPPORTED as of February 2025 |
|
||||
| 11.1 | June 2024 | NO LONGER SUPPORTED as of March 2025 |
|
||||
| 11.2 | August 2024 | May 2025 |
|
||||
| 11.3 | October 2024 | July 2025 |
|
||||
| 11.4 | December 2024 | September 2025 |
|
||||
| 11.5 | January 2025 | October 2025 |
|
||||
| 11.6 (Last minor of 11) | March 2025 | June 2026 |
|
||||
| 12.0 | May 2025 | January 2026 |
|
||||
| **Version** | **Release date** | **Support end date** | **Support level** |
|
||||
| ------------------------- | ------------------ | -------------------- | ----------------------------- |
|
||||
| 9.5.x (Last minor of 9) | April 26, 2023 | July 26, 2024 | Supported for Azure Only |
|
||||
| 10.0.x | June 13, 2023 | March 13, 2024 | Not Supported |
|
||||
| 10.1.x | August 22, 2023 | May 22, 2024 | Not Supported |
|
||||
| 10.2.x | October 24, 2023 | July 24, 2024 | Not Supported |
|
||||
| 10.3.x | January 23, 2024 | October 23, 2024 | Not Supported |
|
||||
| 10.4.x (Last minor of 10) | March 5, 2024 | June 5, 2025 | Security & Critical Bugs Only |
|
||||
| 11.0.x | May 14, 2024 | February 14, 2025 | Security & Critical Bugs Only |
|
||||
| 11.1.x | June 25, 2024 | April 23, 2025 | Security & Critical Bugs Only |
|
||||
| 11.2.x | August 27, 2024 | May 27, 2025 | Security & Critical Bugs Only |
|
||||
| 11.3.x | October 22, 2024 | July 22, 2025 | Security & Critical Bugs Only |
|
||||
| 11.4.x | December 5, 2024 | September 5, 2025 | Security & Critical Bugs Only |
|
||||
| 11.5.x | January 28, 2025 | October 28, 2025 | Security & Critical Bugs Only |
|
||||
| 11.6.x (Last minor of 11) | March 25, 2025 | May 25, 2026 | Full Support |
|
||||
| 12.0.x | May 5, 2025 | February 5, 2026 | Full Support until next minor |
|
||||
| 12.1.x | July 22, 2025 | April 22, 2026 | Full Support until next minor |
|
||||
| 12.2.x | September 23, 2025 | June 23, 2026 | Full Support until next minor |
|
||||
| 12.3.x | November 18, 2025 | August 18, 2026 | Full Support until next minor |
|
||||
|
||||
## How are these versions supported?
|
||||
|
||||
The level of support changes as new versions of Grafana are released. Here are a few details to keep in mind:
|
||||
The level of support changes as new versions of Grafana are released. Here are the key details:
|
||||
|
||||
- The current (most recently released) version of Grafana gets the highest level of support. Releases for this version include all the new features along with all bug fixes.
|
||||
- All supported versions receive security patches for vulnerabilities impacting that version.
|
||||
- All supported versions receive patches for bugs that cause critical feature degradation incidents.
|
||||
- **Full Support**:
|
||||
|
||||
- All new features
|
||||
- All bug fixes
|
||||
- Security patches
|
||||
- Regular updates
|
||||
|
||||
- **Security & Critical Bugs Only**:
|
||||
|
||||
- Security vulnerability patches
|
||||
- Critical bug fixes that cause feature degradation
|
||||
- No new features
|
||||
|
||||
- **Not Supported**: Versions beyond their support period receive no updates and should be upgraded.
|
||||
|
||||
Keeping all this in mind, users that want to receive the most recent features and all bug fixes should be on the current (most recently released) version of Grafana.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@test-plugins/extensions-test-app",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack -c ./webpack.config.ts --env production",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@test-plugins/grafana-e2etest-datasource",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack -c ./webpack.config.ts --env production",
|
||||
|
|
2
go.mod
2
go.mod
|
@ -77,7 +77,7 @@ require (
|
|||
github.com/googleapis/go-sql-spanner v1.11.1 // @grafana/grafana-search-and-storage
|
||||
github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group
|
||||
github.com/gorilla/websocket v1.5.3 // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/alerting v0.0.0-20250418140648-7268b2b6d9f1 // @grafana/alerting-backend
|
||||
github.com/grafana/alerting v0.0.0-20250425150043-be11a2ae18bb // @grafana/alerting-backend
|
||||
github.com/grafana/authlib v0.0.0-20250422131730-e8482efe6b8a // @grafana/identity-access-team
|
||||
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team
|
||||
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1565,8 +1565,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg
|
|||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/alerting v0.0.0-20250418140648-7268b2b6d9f1 h1:xdTzIQqFJxzRLgODsFMAT/8LIWtqCXLqltaWo6AmFVA=
|
||||
github.com/grafana/alerting v0.0.0-20250418140648-7268b2b6d9f1/go.mod h1:XTbf+jPVVMC1C2NuSGa3hIVbO+KSQD0qHjiUr1GoWVI=
|
||||
github.com/grafana/alerting v0.0.0-20250425150043-be11a2ae18bb h1:Nkqatz7R7K/2YaK+7pw8CAYgylKfksPVeT7gMARKjwI=
|
||||
github.com/grafana/alerting v0.0.0-20250425150043-be11a2ae18bb/go.mod h1:pMfhRxL2LZ3Pm8iy7VcVsb9CLYuBtjFYbf1oxgx7yFA=
|
||||
github.com/grafana/authlib v0.0.0-20250422131730-e8482efe6b8a h1:irEH0Abl6mKbkPx/xtmB5Wai4ipzEB6hGPNsLya/p9Y=
|
||||
github.com/grafana/authlib v0.0.0-20250422131730-e8482efe6b8a/go.mod h1:PBtQaXwkFu4BAt2aXsR7w8p8NVpdjV5aJYhqRDei9Us=
|
||||
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM=
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"npmClient": "yarn",
|
||||
"version": "12.0.0-pre"
|
||||
"version": "12.1.0-pre"
|
||||
}
|
||||
|
|
17
package.json
17
package.json
|
@ -3,7 +3,7 @@
|
|||
"license": "AGPL-3.0-only",
|
||||
"private": true,
|
||||
"name": "grafana",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"repository": "github:grafana/grafana",
|
||||
"scripts": {
|
||||
"build": "NODE_ENV=production nx exec --verbose -- webpack --config scripts/webpack/webpack.prod.js --progress",
|
||||
|
@ -64,7 +64,8 @@
|
|||
"plugin:build:commit": "nx run-many -t build:commit --projects='tag:scope:plugin'",
|
||||
"plugin:build:dev": "nx run-many -t dev --projects='tag:scope:plugin' --maxParallel=100",
|
||||
"process-specs": "node --experimental-strip-types scripts/process-specs.ts",
|
||||
"generate-apis": "yarn process-specs && rtk-query-codegen-openapi ./scripts/generate-rtk-apis.ts"
|
||||
"generate-apis": "yarn process-specs && rtk-query-codegen-openapi ./scripts/generate-rtk-apis.ts",
|
||||
"generate:api-client": "NODE_OPTIONS='--experimental-strip-types' plop --plopfile public/app/api/generator/plopfile.ts"
|
||||
},
|
||||
"grafana": {
|
||||
"whatsNewUrl": "https://grafana.com/docs/grafana/next/whatsnew/whats-new-in-v%[1]s-%[2]s/",
|
||||
|
@ -168,7 +169,7 @@
|
|||
"crashme": "0.0.15",
|
||||
"css-loader": "7.1.2",
|
||||
"css-minimizer-webpack-plugin": "7.0.0",
|
||||
"cypress": "13.10.0",
|
||||
"cypress": "14.3.2",
|
||||
"cypress-file-upload": "5.0.8",
|
||||
"cypress-recurse": "^1.35.3",
|
||||
"esbuild": "0.25.0",
|
||||
|
@ -216,6 +217,7 @@
|
|||
"nx": "20.7.1",
|
||||
"openapi-types": "^12.1.3",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"plop": "^4.0.1",
|
||||
"postcss": "8.5.1",
|
||||
"postcss-loader": "8.1.1",
|
||||
"postcss-reporter": "7.1.0",
|
||||
|
@ -255,9 +257,10 @@
|
|||
"@emotion/css": "11.13.5",
|
||||
"@emotion/react": "11.14.0",
|
||||
"@fingerprintjs/fingerprintjs": "^3.4.2",
|
||||
"@floating-ui/react": "0.27.7",
|
||||
"@floating-ui/react": "0.27.8",
|
||||
"@formatjs/intl-durationformat": "^0.7.0",
|
||||
"@glideapps/glide-data-grid": "^6.0.0",
|
||||
"@grafana/alerting": "workspace:*",
|
||||
"@grafana/aws-sdk": "0.6.0",
|
||||
"@grafana/azure-sdk": "0.0.7",
|
||||
"@grafana/data": "workspace:*",
|
||||
|
@ -274,8 +277,8 @@
|
|||
"@grafana/plugin-ui": "0.10.5",
|
||||
"@grafana/prometheus": "workspace:*",
|
||||
"@grafana/runtime": "workspace:*",
|
||||
"@grafana/scenes": "6.10.0",
|
||||
"@grafana/scenes-react": "6.10.0",
|
||||
"@grafana/scenes": "6.10.2",
|
||||
"@grafana/scenes-react": "6.10.2",
|
||||
"@grafana/schema": "workspace:*",
|
||||
"@grafana/sql": "workspace:*",
|
||||
"@grafana/ui": "workspace:*",
|
||||
|
@ -344,7 +347,7 @@
|
|||
"lru-cache": "11.0.2",
|
||||
"lru-memoize": "^1.1.0",
|
||||
"lucene": "^2.1.1",
|
||||
"marked": "15.0.6",
|
||||
"marked": "15.0.11",
|
||||
"memoize-one": "6.0.0",
|
||||
"micro-memoize": "^4.1.2",
|
||||
"ml-regression-polynomial": "^3.0.0",
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,9 @@
|
|||
# Grafana Alerting
|
||||
|
||||
> **@grafana/alerting is currently in ALPHA**.
|
||||
|
||||
This package is a collection of types, libraries, utilities, React components and hooks for interacting with the Grafana alerting system and is meant to be the foundation of alerting-related features in Grafana and its plugins.
|
||||
|
||||
We plan to eventually publish this package on NPM; however, it is currently in ALPHA and is not yet ready for public consumption. We are actively working on this package and will be making breaking changes to it as we iterate on the design and implementation.
|
||||
|
||||
Once we've settled on a public API that we feel is stable and useful, we will publish this package on NPM and provide documentation on how to use it.
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/alerting",
|
||||
"version": "12.0.0-pre",
|
||||
"private": true,
|
||||
"description": "Grafana Alerting Library – Build vertical integrations on top of the industry-leading alerting solution",
|
||||
"keywords": [
|
||||
"typescript",
|
||||
"grafana",
|
||||
"alerting",
|
||||
"alertmanager",
|
||||
"prometheus"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/grafana/grafana.git",
|
||||
"directory": "packages/grafana-alerting"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"module": "src/index.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.ts",
|
||||
"require": "./src/index.ts"
|
||||
},
|
||||
"./internal": {
|
||||
"import": "./src/internal.ts",
|
||||
"require": "./src/internal.ts"
|
||||
},
|
||||
"./unstable": {
|
||||
"import": "./src/unstable.ts",
|
||||
"require": "./src/unstable.ts"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsc --emitDeclarationOnly false --noEmit",
|
||||
"codegen": "yarn run rtk-query-codegen-openapi ./scripts/codegen.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@grafana/tsconfig": "^2.0.0",
|
||||
"@rtk-query/codegen-openapi": "^2.0.0",
|
||||
"@types/lodash": "^4",
|
||||
"@types/react": "18.3.18",
|
||||
"@types/react-dom": "18.3.5",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"type-fest": "^4.40.0",
|
||||
"typescript": "5.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@grafana/runtime": "^12.0.0-pre",
|
||||
"@grafana/ui": "^12.0.0-pre",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^2.7.0",
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "library",
|
||||
"tags": ["scope:package", "type:ui"],
|
||||
"targets": {}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* This script will generate TypeScript type definitions and a RTKQ client for the alerting k8s APIs.
|
||||
* It downloads the OpenAPI schema from a running Grafana instance and generates the types.
|
||||
*
|
||||
* Run `yarn run codegen` from the "grafana-alerting" package to invoke this script.
|
||||
*/
|
||||
import { type ConfigFile } from '@rtk-query/codegen-openapi';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
// these snapshots are generated by running "go test pkg/tests/apis/openapi_test.go", see the README in the "openapi_snapshots" directory
|
||||
const OPENAPI_SCHEMA_LOCATION = resolve(
|
||||
'../../../pkg/tests/apis/openapi_snapshots/notifications.alerting.grafana.app-v0alpha1.json'
|
||||
);
|
||||
|
||||
export default {
|
||||
exportName: 'alertingAPI',
|
||||
schemaFile: OPENAPI_SCHEMA_LOCATION,
|
||||
apiFile: '../src/grafana/api.ts',
|
||||
outputFile: resolve('../src/grafana/api.gen.ts'),
|
||||
tag: true,
|
||||
} satisfies ConfigFile;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,11 @@
|
|||
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
const BASE_URL = '/';
|
||||
|
||||
export const api = createApi({
|
||||
reducerPath: 'grafanaAlertingAPI',
|
||||
baseQuery: fetchBaseQuery({
|
||||
baseUrl: BASE_URL,
|
||||
}),
|
||||
endpoints: () => ({}),
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
import { chain } from 'lodash';
|
||||
|
||||
import { Combobox, ComboboxOption } from '@grafana/ui';
|
||||
|
||||
import { useListContactPoints } from '../hooks/useContactPoints';
|
||||
import { ContactPoint } from '../types';
|
||||
import { getContactPointDescription } from '../utils';
|
||||
|
||||
const collator = new Intl.Collator('en', { sensitivity: 'accent' });
|
||||
|
||||
type ContactPointSelectorProps = {
|
||||
onChange: (contactPoint: ContactPoint) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contact Point Combobox which lists all available contact points
|
||||
* @TODO make ComboBox accept a ReactNode so we can use icons and such
|
||||
*/
|
||||
function ContactPointSelector({ onChange }: ContactPointSelectorProps) {
|
||||
const { currentData: contactPoints, isLoading } = useListContactPoints();
|
||||
|
||||
// Create a mapping of options with their corresponding contact points
|
||||
const contactPointOptions = chain(contactPoints?.items)
|
||||
.toArray()
|
||||
.map((contactPoint) => ({
|
||||
option: {
|
||||
label: contactPoint.spec.title,
|
||||
value: contactPoint.metadata.uid ?? contactPoint.spec.title,
|
||||
description: getContactPointDescription(contactPoint),
|
||||
},
|
||||
contactPoint,
|
||||
}))
|
||||
.value()
|
||||
.sort((a, b) => collator.compare(a.option.label, b.option.label));
|
||||
|
||||
const options = contactPointOptions.map<ComboboxOption>((item) => item.option);
|
||||
|
||||
const handleChange = ({ value }: ComboboxOption<string>) => {
|
||||
const selectedItem = contactPointOptions.find(({ option }) => option.value === value);
|
||||
if (!selectedItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
onChange(selectedItem.contactPoint);
|
||||
};
|
||||
|
||||
return <Combobox loading={isLoading} onChange={handleChange} options={options} />;
|
||||
}
|
||||
|
||||
export { ContactPointSelector };
|
|
@ -0,0 +1,29 @@
|
|||
import { fetchBaseQuery, TypedUseQueryHookResult } from '@reduxjs/toolkit/query/react';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { alertingAPI, ListReceiverApiArg } from '../../api.gen';
|
||||
import { EnhancedListReceiverResponse } from '../types';
|
||||
|
||||
const { namespace } = config;
|
||||
|
||||
// this is a workaround for the fact that the generated types are not narrow enough
|
||||
type EnhancedHookResult = TypedUseQueryHookResult<
|
||||
EnhancedListReceiverResponse,
|
||||
ListReceiverApiArg,
|
||||
ReturnType<typeof fetchBaseQuery>
|
||||
>;
|
||||
|
||||
/**
|
||||
* useListContactPoints is a hook that fetches a list of contact points
|
||||
*
|
||||
* This function wraps the alertingAPI.useListReceiverQuery with proper typing
|
||||
* to ensure that the returned ContactPoints are correctly typed in the data.items array.
|
||||
*
|
||||
* It automatically uses the configured namespace for the query.
|
||||
*/
|
||||
function useListContactPoints() {
|
||||
return alertingAPI.useListReceiverQuery<EnhancedHookResult>({ namespace });
|
||||
}
|
||||
|
||||
export { useListContactPoints };
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* This file contains enhanced and type-narrowed versions of the types generated by the RTKQ codegen package.
|
||||
*/
|
||||
import { MergeDeep, MergeExclusive, OverrideProperties } from 'type-fest';
|
||||
|
||||
import {
|
||||
ComGithubGrafanaGrafanaAppsAlertingNotificationsPkgApisReceiverV0Alpha1Receiver as ContactPointV0Alpha1,
|
||||
ComGithubGrafanaGrafanaAppsAlertingNotificationsPkgApisReceiverV0Alpha1Integration as IntegrationV0Alpha1,
|
||||
ListReceiverApiResponse,
|
||||
} from '../api.gen';
|
||||
|
||||
type GenericIntegration = OverrideProperties<
|
||||
IntegrationV0Alpha1,
|
||||
{
|
||||
settings: Record<string, unknown>;
|
||||
}
|
||||
>;
|
||||
|
||||
// Based on https://github.com/grafana/alerting/blob/main/receivers/email/config.go#L20-L25
|
||||
type EmailIntegration = OverrideProperties<
|
||||
GenericIntegration,
|
||||
{
|
||||
type: 'email';
|
||||
settings: {
|
||||
singleEmail?: boolean;
|
||||
addresses: string;
|
||||
message?: string;
|
||||
subject?: string;
|
||||
};
|
||||
secureFields: never; // email doesn't have any secure fields
|
||||
}
|
||||
>;
|
||||
|
||||
// Based on https://github.com/grafana/alerting/blob/main/receivers/slack/config.go
|
||||
type SlackIntegration = OverrideProperties<
|
||||
GenericIntegration,
|
||||
{
|
||||
type: 'slack';
|
||||
settings: {
|
||||
endpointUrl?: string;
|
||||
url?: string;
|
||||
recipient?: string;
|
||||
text?: string;
|
||||
title?: string;
|
||||
username?: string;
|
||||
icon_emoji?: string;
|
||||
icon_url?: string;
|
||||
mentionChannel?: string;
|
||||
mentionUsers?: string; // comma separated string
|
||||
mentionGroups?: string; // comma separated string
|
||||
color?: string;
|
||||
};
|
||||
// secureFields is a union type that can be either a token or a URL but you can't have both
|
||||
secureFields: MergeExclusive<{ token: string }, { url: string }>;
|
||||
}
|
||||
>;
|
||||
|
||||
export type Integration = EmailIntegration | SlackIntegration | GenericIntegration;
|
||||
|
||||
// Enhanced version of ContactPoint with typed integrations
|
||||
// ⚠️ MergeDeep does not check if the property you are overriding exists in the base type and there is no "DeepOverrideProperties" helper
|
||||
export type ContactPoint = MergeDeep<
|
||||
ContactPointV0Alpha1,
|
||||
{
|
||||
spec: {
|
||||
integrations: Integration[];
|
||||
};
|
||||
}
|
||||
>;
|
||||
|
||||
export type EnhancedListReceiverResponse = OverrideProperties<
|
||||
ListReceiverApiResponse,
|
||||
{
|
||||
items: ContactPoint[];
|
||||
}
|
||||
>;
|
|
@ -0,0 +1,31 @@
|
|||
import { countBy, isEmpty } from 'lodash';
|
||||
|
||||
import { ContactPoint } from './types';
|
||||
|
||||
/**
|
||||
* Generates a human-readable description of a ContactPoint by summarizing its integrations.
|
||||
* If the ContactPoint has no integrations, it returns an empty placeholder text.
|
||||
*
|
||||
* For integrations, it counts the occurrences of each type and formats them as a comma-separated list.
|
||||
* Multiple integrations of the same type are indicated with a count in parentheses.
|
||||
*
|
||||
* @param contactPoint - The ContactPoint object to describe
|
||||
* @returns A string description of the ContactPoint's integrations
|
||||
*/
|
||||
export function getContactPointDescription(contactPoint: ContactPoint): string {
|
||||
if (isEmpty(contactPoint.spec.integrations)) {
|
||||
return '<empty contact point>';
|
||||
}
|
||||
|
||||
// Count the occurrences of each integration type
|
||||
const integrationCounts = countBy(contactPoint.spec.integrations, (integration) => integration.type);
|
||||
|
||||
const description = Object.entries(integrationCounts)
|
||||
.map(([type, count]) => {
|
||||
// either "email" or "email (2)" but not "email (1)"
|
||||
return count > 1 ? `${type} (${count})` : type;
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
return description;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Export things here that you want to be available under @grafana/alerting
|
||||
*
|
||||
* ⚠️ This implies everything in here is public API and should be considered stable and treated as such – make sure to
|
||||
* think carefully about what you export here and the interfaces / data structures.
|
||||
*
|
||||
* Breaking changes should be avoided to maintain backwards compatibility for consumers of this package.
|
||||
*/
|
||||
export default {};
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* Export things here that you want to be available under @grafana/alerting/internal
|
||||
*/
|
||||
export { alertingAPI } from './grafana/api.gen';
|
||||
|
||||
export default {};
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* Export things here that you want to be available under @grafana/alerting/unstable
|
||||
*/
|
||||
|
||||
// Contact Points
|
||||
export * from './grafana/contactPoints/types';
|
||||
export { useListContactPoints } from './grafana/contactPoints/hooks/useContactPoints';
|
||||
export { ContactPointSelector } from './grafana/contactPoints/components/ContactPointSelector';
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"exclude": ["dist", "node_modules", "test", "**/*.test.ts*"],
|
||||
"extends": "./tsconfig.json"
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"declarationDir": "./compiled",
|
||||
"emitDeclarationOnly": true,
|
||||
"isolatedModules": true,
|
||||
"rootDirs": ["."]
|
||||
},
|
||||
"exclude": ["dist/**/*"],
|
||||
"extends": "@grafana/tsconfig",
|
||||
"include": ["typings/jest", "../../public/app/types/*.d.ts", "../grafana-ui/src/types/*.d.ts", "src/**/*.ts*"]
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/data",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"description": "Grafana Data Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
|
@ -56,7 +56,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "7.0.1",
|
||||
"@grafana/schema": "12.0.0-pre",
|
||||
"@grafana/schema": "12.1.0-pre",
|
||||
"@types/d3-interpolate": "^3.0.0",
|
||||
"@types/string-hash": "1.1.3",
|
||||
"@types/systemjs": "6.15.1",
|
||||
|
@ -67,7 +67,7 @@
|
|||
"fast_array_intersect": "1.1.0",
|
||||
"history": "4.10.1",
|
||||
"lodash": "4.17.21",
|
||||
"marked": "15.0.6",
|
||||
"marked": "15.0.11",
|
||||
"marked-mangle": "1.1.10",
|
||||
"moment": "2.30.1",
|
||||
"moment-timezone": "0.5.47",
|
||||
|
|
|
@ -354,11 +354,6 @@ export interface FeatureToggles {
|
|||
*/
|
||||
cloudWatchBatchQueries?: boolean;
|
||||
/**
|
||||
* Enables feature recovery threshold (aka hysteresis) for threshold server-side expression
|
||||
* @default true
|
||||
*/
|
||||
recoveryThreshold?: boolean;
|
||||
/**
|
||||
* Enables the loki data source to request structured metadata from the Loki server
|
||||
* @default true
|
||||
*/
|
||||
|
@ -1019,6 +1014,10 @@ export interface FeatureToggles {
|
|||
*/
|
||||
pluginsAutoUpdate?: boolean;
|
||||
/**
|
||||
* Register MT frontend
|
||||
*/
|
||||
multiTenantFrontend?: boolean;
|
||||
/**
|
||||
* Enables the alerting list view v2 preview toggle
|
||||
*/
|
||||
alertingListViewV2PreviewToggle?: boolean;
|
||||
|
@ -1027,4 +1026,9 @@ export interface FeatureToggles {
|
|||
* @default false
|
||||
*/
|
||||
alertRuleUseFiredAtForStartsAt?: boolean;
|
||||
/**
|
||||
* Enables the alerting bulk actions in the UI
|
||||
* @default true
|
||||
*/
|
||||
alertingBulkActionsInUI?: boolean;
|
||||
}
|
||||
|
|
|
@ -139,6 +139,7 @@ export const availableIconsIndex = {
|
|||
'gf-layout-simple': true,
|
||||
'gf-logs': true,
|
||||
'gf-ml': true,
|
||||
'gf-ml-alt': true,
|
||||
'gf-movepane-left': true,
|
||||
'gf-movepane-right': true,
|
||||
'gf-portrait': true,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e-selectors",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"description": "Grafana End-to-End Test Selectors Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@grafana/eslint-plugin",
|
||||
"description": "ESLint rules for use within the Grafana repo. Not suitable (or supported) for external use.",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"main": "./index.cjs",
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/flamegraph",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"description": "Grafana flamegraph visualization component",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
|
@ -44,8 +44,8 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.13.5",
|
||||
"@grafana/data": "12.0.0-pre",
|
||||
"@grafana/ui": "12.0.0-pre",
|
||||
"@grafana/data": "12.1.0-pre",
|
||||
"@grafana/ui": "12.1.0-pre",
|
||||
"@leeoniya/ufuzzy": "1.0.18",
|
||||
"d3": "^7.8.5",
|
||||
"lodash": "4.17.21",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"license": "AGPL-3.0-only",
|
||||
"name": "@grafana/o11y-ds-frontend",
|
||||
"private": true,
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"description": "Library to manage traces in Grafana.",
|
||||
"sideEffects": false,
|
||||
"repository": {
|
||||
|
@ -18,12 +18,12 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.13.5",
|
||||
"@grafana/data": "12.0.0-pre",
|
||||
"@grafana/e2e-selectors": "12.0.0-pre",
|
||||
"@grafana/data": "12.1.0-pre",
|
||||
"@grafana/e2e-selectors": "12.1.0-pre",
|
||||
"@grafana/plugin-ui": "0.10.5",
|
||||
"@grafana/runtime": "12.0.0-pre",
|
||||
"@grafana/schema": "12.0.0-pre",
|
||||
"@grafana/ui": "12.0.0-pre",
|
||||
"@grafana/runtime": "12.1.0-pre",
|
||||
"@grafana/schema": "12.1.0-pre",
|
||||
"@grafana/ui": "12.1.0-pre",
|
||||
"react-select": "5.10.0",
|
||||
"react-use": "17.6.0",
|
||||
"rxjs": "7.8.1",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@grafana/plugin-configs",
|
||||
"description": "Shared dependencies and files for core plugins",
|
||||
"private": true,
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"dependencies": {
|
||||
"tslib": "2.8.1"
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"author": "Grafana Labs",
|
||||
"license": "AGPL-3.0-only",
|
||||
"name": "@grafana/prometheus",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"description": "Grafana Prometheus Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
|
@ -37,13 +37,13 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.13.5",
|
||||
"@floating-ui/react": "0.27.7",
|
||||
"@grafana/data": "12.0.0-pre",
|
||||
"@grafana/e2e-selectors": "12.0.0-pre",
|
||||
"@floating-ui/react": "0.27.8",
|
||||
"@grafana/data": "12.1.0-pre",
|
||||
"@grafana/e2e-selectors": "12.1.0-pre",
|
||||
"@grafana/plugin-ui": "0.10.5",
|
||||
"@grafana/runtime": "12.0.0-pre",
|
||||
"@grafana/schema": "12.0.0-pre",
|
||||
"@grafana/ui": "12.0.0-pre",
|
||||
"@grafana/runtime": "12.1.0-pre",
|
||||
"@grafana/schema": "12.1.0-pre",
|
||||
"@grafana/ui": "12.1.0-pre",
|
||||
"@hello-pangea/dnd": "17.0.0",
|
||||
"@leeoniya/ufuzzy": "1.0.18",
|
||||
"@lezer/common": "1.2.3",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/runtime",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"description": "Grafana Runtime Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
|
@ -53,11 +53,11 @@
|
|||
"postpack": "mv package.json.bak package.json && rimraf ./unstable"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/data": "12.0.0-pre",
|
||||
"@grafana/e2e-selectors": "12.0.0-pre",
|
||||
"@grafana/data": "12.1.0-pre",
|
||||
"@grafana/e2e-selectors": "12.1.0-pre",
|
||||
"@grafana/faro-web-sdk": "^1.13.2",
|
||||
"@grafana/schema": "12.0.0-pre",
|
||||
"@grafana/ui": "12.0.0-pre",
|
||||
"@grafana/schema": "12.1.0-pre",
|
||||
"@grafana/ui": "12.1.0-pre",
|
||||
"@types/systemjs": "6.15.1",
|
||||
"history": "4.10.1",
|
||||
"lodash": "4.17.21",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/schema",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"description": "Grafana Schema Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options {
|
||||
limit: number;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip, common.OptionsWithTextFormatting {
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options extends common.OptionsWithLegend, common.SingleStatBaseOptions {
|
||||
displayMode: common.BarGaugeDisplayMode;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export enum VizDisplayMode {
|
||||
Candles = 'candles',
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export enum HorizontalConstraint {
|
||||
Center = 'center',
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface MetricStat {
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options {
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options {
|
||||
selectedSeries: number;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export type UpdateConfig = {
|
||||
render: boolean,
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export type BucketAggregation = (DateHistogram | Histogram | Terms | Filters | GeoHashGrid | Nested);
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options extends common.SingleStatBaseOptions {
|
||||
minVizHeight: number;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options {
|
||||
basemap: ui.MapLayerOptions;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
/**
|
||||
* Controls the color mode of the heatmap
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip {
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options {
|
||||
controlsStorageKey?: string;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options {
|
||||
dedupStrategy: common.LogsDedupStrategy;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export enum QueryEditorMode {
|
||||
Builder = 'builder',
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options {
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface ArcOption {
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
/**
|
||||
* Select the pie chart display style.
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options extends common.SingleStatBaseOptions {
|
||||
colorMode: common.BigValueColorMode;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones {
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones {
|
||||
/**
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options {
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export enum TextMode {
|
||||
Code = 'code',
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export interface Options extends common.OptionsWithTimezones {
|
||||
legend: common.VizLegendOptions;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
/**
|
||||
* Identical to timeseries... except it does not have timezone settings
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "12.0.0-pre";
|
||||
export const pluginVersion = "12.1.0-pre";
|
||||
|
||||
export enum PointShape {
|
||||
Circle = 'circle',
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"license": "AGPL-3.0-only",
|
||||
"private": true,
|
||||
"name": "@grafana/sql",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/grafana/grafana.git",
|
||||
|
@ -15,11 +15,11 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.13.5",
|
||||
"@grafana/data": "12.0.0-pre",
|
||||
"@grafana/e2e-selectors": "12.0.0-pre",
|
||||
"@grafana/data": "12.1.0-pre",
|
||||
"@grafana/e2e-selectors": "12.1.0-pre",
|
||||
"@grafana/plugin-ui": "0.10.5",
|
||||
"@grafana/runtime": "12.0.0-pre",
|
||||
"@grafana/ui": "12.0.0-pre",
|
||||
"@grafana/runtime": "12.1.0-pre",
|
||||
"@grafana/ui": "12.1.0-pre",
|
||||
"@react-awesome-query-builder/ui": "6.6.14",
|
||||
"immutable": "5.0.3",
|
||||
"lodash": "4.17.21",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/ui",
|
||||
"version": "12.0.0-pre",
|
||||
"version": "12.1.0-pre",
|
||||
"description": "Grafana Components Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
|
@ -65,11 +65,11 @@
|
|||
"@emotion/css": "11.13.5",
|
||||
"@emotion/react": "11.14.0",
|
||||
"@emotion/serialize": "1.3.3",
|
||||
"@floating-ui/react": "0.27.7",
|
||||
"@grafana/data": "12.0.0-pre",
|
||||
"@grafana/e2e-selectors": "12.0.0-pre",
|
||||
"@floating-ui/react": "0.27.8",
|
||||
"@grafana/data": "12.1.0-pre",
|
||||
"@grafana/e2e-selectors": "12.1.0-pre",
|
||||
"@grafana/faro-web-sdk": "^1.13.2",
|
||||
"@grafana/schema": "12.0.0-pre",
|
||||
"@grafana/schema": "12.1.0-pre",
|
||||
"@hello-pangea/dnd": "17.0.0",
|
||||
"@leeoniya/ufuzzy": "1.0.18",
|
||||
"@monaco-editor/react": "4.6.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { css } from '@emotion/css';
|
||||
import { autoUpdate, flip, shift, useClick, useDismiss, useFloating, useInteractions } from '@floating-ui/react';
|
||||
import { ChangeEvent, useState } from 'react';
|
||||
import { ChangeEvent, forwardRef, useImperativeHandle, useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, dateTime } from '@grafana/data';
|
||||
|
||||
|
@ -11,7 +11,7 @@ import { DatePicker } from '../DatePicker/DatePicker';
|
|||
export const formatDate = (date: Date | string) => dateTime(date).format('L');
|
||||
|
||||
/** @public */
|
||||
export interface DatePickerWithInputProps extends Omit<InputProps, 'ref' | 'value' | 'onChange'> {
|
||||
export interface DatePickerWithInputProps extends Omit<InputProps, 'value' | 'onChange'> {
|
||||
/** Value selected by the DatePicker */
|
||||
value?: Date | string;
|
||||
/** The minimum date the value can be set to */
|
||||
|
@ -27,78 +27,78 @@ export interface DatePickerWithInputProps extends Omit<InputProps, 'ref' | 'valu
|
|||
}
|
||||
|
||||
/** @public */
|
||||
export const DatePickerWithInput = ({
|
||||
value,
|
||||
minDate,
|
||||
maxDate,
|
||||
onChange,
|
||||
closeOnSelect,
|
||||
placeholder = 'Date',
|
||||
...rest
|
||||
}: DatePickerWithInputProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const styles = useStyles2(getStyles);
|
||||
export const DatePickerWithInput = forwardRef<HTMLInputElement, DatePickerWithInputProps>(
|
||||
({ value, minDate, maxDate, onChange, closeOnSelect, placeholder = 'Date', ...rest }, ref) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
// the order of middleware is important!
|
||||
// see https://floating-ui.com/docs/arrow#order
|
||||
const middleware = [
|
||||
flip({
|
||||
// see https://floating-ui.com/docs/flip#combining-with-shift
|
||||
crossAxis: false,
|
||||
boundary: document.body,
|
||||
}),
|
||||
shift(),
|
||||
];
|
||||
// the order of middleware is important!
|
||||
// see https://floating-ui.com/docs/arrow#order
|
||||
const middleware = [
|
||||
flip({
|
||||
// see https://floating-ui.com/docs/flip#combining-with-shift
|
||||
crossAxis: false,
|
||||
boundary: document.body,
|
||||
}),
|
||||
shift(),
|
||||
];
|
||||
|
||||
const { context, refs, floatingStyles } = useFloating({
|
||||
open,
|
||||
placement: 'bottom-start',
|
||||
onOpenChange: setOpen,
|
||||
middleware,
|
||||
whileElementsMounted: autoUpdate,
|
||||
strategy: 'fixed',
|
||||
});
|
||||
const { context, refs, floatingStyles } = useFloating<HTMLInputElement>({
|
||||
open,
|
||||
placement: 'bottom-start',
|
||||
onOpenChange: setOpen,
|
||||
middleware,
|
||||
whileElementsMounted: autoUpdate,
|
||||
strategy: 'fixed',
|
||||
});
|
||||
|
||||
const click = useClick(context);
|
||||
const dismiss = useDismiss(context);
|
||||
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, click]);
|
||||
const click = useClick(context);
|
||||
const dismiss = useDismiss(context);
|
||||
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss, click]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Input
|
||||
ref={refs.setReference}
|
||||
type="text"
|
||||
autoComplete={'off'}
|
||||
placeholder={placeholder}
|
||||
value={value ? formatDate(value) : value}
|
||||
onChange={(ev: ChangeEvent<HTMLInputElement>) => {
|
||||
// Allow resetting the date
|
||||
if (ev.target.value === '') {
|
||||
onChange('');
|
||||
}
|
||||
}}
|
||||
className={styles.input}
|
||||
{...rest}
|
||||
{...getReferenceProps()}
|
||||
/>
|
||||
<div className={styles.popover} ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>
|
||||
<DatePicker
|
||||
isOpen={open}
|
||||
value={value && typeof value !== 'string' ? value : dateTime().toDate()}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
onChange={(ev) => {
|
||||
onChange(ev);
|
||||
if (closeOnSelect) {
|
||||
setOpen(false);
|
||||
useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(ref, () => refs.domReference.current, [
|
||||
refs.domReference,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Input
|
||||
ref={refs.setReference}
|
||||
type="text"
|
||||
autoComplete={'off'}
|
||||
placeholder={placeholder}
|
||||
value={value ? formatDate(value) : value}
|
||||
onChange={(ev: ChangeEvent<HTMLInputElement>) => {
|
||||
// Allow resetting the date
|
||||
if (ev.target.value === '') {
|
||||
onChange('');
|
||||
}
|
||||
}}
|
||||
onClose={() => setOpen(false)}
|
||||
className={styles.input}
|
||||
{...rest}
|
||||
{...getReferenceProps()}
|
||||
/>
|
||||
<div className={styles.popover} ref={refs.setFloating} style={floatingStyles} {...getFloatingProps()}>
|
||||
<DatePicker
|
||||
isOpen={open}
|
||||
value={value && typeof value !== 'string' ? value : dateTime().toDate()}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
onChange={(ev) => {
|
||||
onChange(ev);
|
||||
if (closeOnSelect) {
|
||||
setOpen(false);
|
||||
}
|
||||
}}
|
||||
onClose={() => setOpen(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
DatePickerWithInput.displayName = 'DatePickerWithInput';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
|
|
|
@ -5,6 +5,6 @@ import { Icon } from '../Icon/Icon';
|
|||
export function DropdownIndicator({ selectProps }: DropdownIndicatorProps) {
|
||||
const isOpen = selectProps.menuIsOpen;
|
||||
const icon = isOpen ? 'search' : 'angle-down';
|
||||
const size = isOpen ? 'sm' : 'md';
|
||||
const size = 'md';
|
||||
return <Icon name={icon} size={size} />;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { isArray, negate } from 'lodash';
|
||||
import { ComponentProps, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { ComponentProps, useCallback, useEffect, useRef, useState, useImperativeHandle } from 'react';
|
||||
import * as React from 'react';
|
||||
import {
|
||||
default as ReactSelect,
|
||||
|
@ -152,16 +152,19 @@ export function SelectBase<T, Rest = {}>({
|
|||
isValidNewOption,
|
||||
formatOptionLabel,
|
||||
hideSelectedOptions,
|
||||
selectRef,
|
||||
...rest
|
||||
}: SelectBaseProps<T> & Rest) {
|
||||
const theme = useTheme2();
|
||||
const styles = getSelectStyles(theme);
|
||||
|
||||
const reactSelectRef = useRef<{ controlRef: HTMLElement }>(null);
|
||||
const reactSelectRef = useRef<HTMLElement & { controlRef: HTMLElement }>(null);
|
||||
const [closeToBottom, setCloseToBottom] = useState<boolean>(false);
|
||||
const selectStyles = useCustomSelectStyles(theme, width);
|
||||
const [hasInputValue, setHasInputValue] = useState<boolean>(!!inputValue);
|
||||
|
||||
useImperativeHandle(selectRef, () => reactSelectRef.current!, []);
|
||||
|
||||
// Infer the menu position for asynchronously loaded options. menuPlacement="auto" doesn't work when the menu is
|
||||
// automatically opened when the component is created (it happens in SegmentSelect by setting menuIsOpen={true}).
|
||||
// We can remove this workaround when the bug in react-select is fixed: https://github.com/JedWatson/react-select/issues/4936
|
||||
|
|
|
@ -116,6 +116,8 @@ export interface SelectCommonProps<T> {
|
|||
loadingMessage?: string;
|
||||
/** Disables wrapping of multi value values when closed */
|
||||
noMultiValueWrap?: boolean;
|
||||
/** Use a custom ref because generic component as output of React.forwardRef is not directly possible */
|
||||
selectRef?: React.Ref<HTMLElement>;
|
||||
}
|
||||
|
||||
export interface SelectAsyncProps<T> {
|
||||
|
|
|
@ -48,6 +48,7 @@ import (
|
|||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/frontend"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol"
|
||||
publicdashboardsapi "github.com/grafana/grafana/pkg/services/publicdashboards/api"
|
||||
|
@ -85,6 +86,17 @@ func (hs *HTTPServer) registerRoutes() {
|
|||
r.Get("/login", hs.LoginView)
|
||||
r.Get("/invite/:code", hs.Index)
|
||||
|
||||
if hs.Features.IsEnabledGlobally(featuremgmt.FlagMultiTenantFrontend) {
|
||||
index, err := frontend.NewIndexProvider(hs.Cfg, hs.License)
|
||||
if err != nil {
|
||||
panic(err) // ???
|
||||
}
|
||||
r.Get("/mtfe", index.HandleRequest)
|
||||
|
||||
// Temporarily expose the full bootdata via API
|
||||
r.Get("/bootdata", reqNoAuth, hs.GetBootdata)
|
||||
}
|
||||
|
||||
// authed views
|
||||
r.Get("/", reqSignedIn, hs.Index)
|
||||
r.Get("/profile/", reqSignedInNoAnonymous, hs.Index)
|
||||
|
|
|
@ -536,12 +536,6 @@ func (hs *HTTPServer) postDashboard(c *contextmodel.ReqContext, cmd dashboards.S
|
|||
return apierrors.ToDashboardErrorResponse(ctx, hs.pluginStore, saveErr)
|
||||
}
|
||||
|
||||
// Clear permission cache for the user who's created the dashboard, so that new permissions are fetched for their next call
|
||||
// Required for cases when caller wants to immediately interact with the newly created object
|
||||
if newDashboard {
|
||||
hs.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
|
||||
}
|
||||
|
||||
// connect library panels for this dashboard after the dashboard is stored and has an ID
|
||||
err = hs.LibraryPanelService.ConnectLibraryPanelsForDashboard(ctx, c.SignedInUser, dashboard)
|
||||
if err != nil {
|
||||
|
|
|
@ -946,7 +946,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
|
|||
if dashboardService == nil {
|
||||
dashboardService, err = service.ProvideDashboardServiceImpl(
|
||||
cfg, dashboardStore, folderStore, features, folderPermissions,
|
||||
ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil,
|
||||
ac, actest.FakeService{}, folderSvc, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil,
|
||||
dualwrite.ProvideTestService(), sort.ProvideService(),
|
||||
serverlock.ProvideService(db, tracing.InitializeTracerForTest()), kvstore.NewFakeKVStore(),
|
||||
)
|
||||
|
@ -956,7 +956,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
|
|||
|
||||
dashboardProvisioningService, err := service.ProvideDashboardServiceImpl(
|
||||
cfg, dashboardStore, folderStore, features, folderPermissions,
|
||||
ac, folderSvc, fStore, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil,
|
||||
ac, actest.FakeService{}, folderSvc, nil, client.MockTestRestConfig{}, nil, quotaService, nil, nil, nil,
|
||||
dualwrite.ProvideTestService(), sort.ProvideService(),
|
||||
serverlock.ProvideService(db, tracing.InitializeTracerForTest()), kvstore.NewFakeKVStore(),
|
||||
)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue