Merge branch 'main' into ismail/refactor-language-provider-methods
This commit is contained in:
@@ -1640,15 +1640,6 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "5"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "6"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/saving/provisioned/SaveProvisionedDashboardForm.tsx:5381": [
|
||||
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"],
|
||||
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"],
|
||||
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"],
|
||||
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"],
|
||||
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "4"],
|
||||
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "5"],
|
||||
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "6"]
|
||||
],
|
||||
"public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
|
||||
+3
-2
@@ -4343,7 +4343,8 @@ steps:
|
||||
- apk add docker
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- dagger run --silent go run ./pkg/build/cmd artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF}
|
||||
--enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --version=$${VERSION}
|
||||
--enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --build-id=$${DRONE_BUILD_NUMBER}
|
||||
--version=$${VERSION}
|
||||
depends_on:
|
||||
- github-app-generate-token
|
||||
environment:
|
||||
@@ -5205,6 +5206,6 @@ kind: secret
|
||||
name: gcr_credentials
|
||||
---
|
||||
kind: signature
|
||||
hmac: 1da5c9da34dc5dbffef8bef4ef84282997cb2363f929fdf4b897d2ee87a33703
|
||||
hmac: e430c51190ba46dbd426d13f47c13c275f4d05b6b1a26d1d9f67b5a8f09ecb41
|
||||
|
||||
...
|
||||
|
||||
+2
-1
@@ -177,6 +177,7 @@
|
||||
/pkg/tests/apis/query @grafana/grafana-datasources-core-services
|
||||
/pkg/tests/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend
|
||||
/pkg/tests/api/correlations/ @grafana/datapro
|
||||
/pkg/tests/apis/secret/ @grafana/grafana-operator-experience-squad
|
||||
/pkg/tsdb/grafanads/ @grafana/grafana-backend-group
|
||||
/pkg/tsdb/opentsdb/ @grafana/partner-datasources
|
||||
/pkg/util/ @grafana/grafana-backend-group
|
||||
@@ -485,7 +486,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/public/app/core/components/TimelineChart/ @grafana/dataviz-squad
|
||||
/public/app/core/components/Form/ @grafana/grafana-frontend-platform
|
||||
/public/app/core/components/OptionsUI/ @grafana/dashboards-squad @grafana/dataviz-squad
|
||||
/public/app/mock-api-utils.ts @grafana/grafana-frontend-platform
|
||||
/public/app/dev-utils.ts @grafana/grafana-frontend-platform
|
||||
|
||||
/public/app/core/history/ @grafana/observability-traces-and-profiling
|
||||
/public/app/features/admin/ @grafana/identity-access-team
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
"name": "datasource/Elasticsearch",
|
||||
"action": "addToProject",
|
||||
"addToProject": {
|
||||
"url": "https://github.com/orgs/grafana/projects/97"
|
||||
"url": "https://github.com/orgs/grafana/projects/190"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -42,10 +42,12 @@ jobs:
|
||||
GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY
|
||||
- name: "Generate token"
|
||||
id: generate_token
|
||||
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92
|
||||
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
|
||||
with:
|
||||
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
|
||||
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
|
||||
repositories: "[\"grafana\", \"grafana-enterprise\"]"
|
||||
permissions: "{\"contents\": \"write\", \"pull_requests\": \"write\", \"workflows\":\"write\"}"
|
||||
- name: Create release branch
|
||||
id: branch
|
||||
uses: grafana/grafana-github-actions-go/bump-release@main # zizmor: ignore[unpinned-uses]
|
||||
|
||||
@@ -3,8 +3,9 @@ name: Crowdin automatic task management
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# once a week on Sunday at midnight
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
# TODO enable once we're ready to create tasks automatically
|
||||
# schedule:
|
||||
# - cron: "0 0 * * 0"
|
||||
|
||||
jobs:
|
||||
create-tasks-in-crowdin:
|
||||
|
||||
@@ -44,13 +44,6 @@ jobs:
|
||||
VERSION: ${{ inputs.version }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Get vault secrets"
|
||||
id: vault-secrets
|
||||
uses: grafana/shared-workflows/actions/get-vault-secrets@main
|
||||
with:
|
||||
# Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault
|
||||
repo_secrets: |
|
||||
GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY
|
||||
- if: ${{ github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/') }}
|
||||
run: |
|
||||
{
|
||||
@@ -73,14 +66,14 @@ jobs:
|
||||
create_next_release_branch_grafana:
|
||||
name: Create next release branch (Grafana)
|
||||
needs: setup
|
||||
uses: ./.github/workflows/create-next-release-branch.yml
|
||||
uses: grafana/grafana/.github/workflows/create-next-release-branch.yml@main
|
||||
with:
|
||||
ownerRepo: 'grafana/grafana'
|
||||
source: ${{ needs.setup.outputs.release_branch }}
|
||||
create_next_release_branch_enterprise:
|
||||
name: Create next release branch (Grafana Enterprise)
|
||||
needs: setup
|
||||
uses: ./.github/workflows/create-next-release-branch.yml
|
||||
uses: grafana/grafana/.github/workflows/create-next-release-branch.yml@main
|
||||
with:
|
||||
ownerRepo: 'grafana/grafana-enterprise'
|
||||
source: ${{ needs.setup.outputs.release_branch }}
|
||||
@@ -88,7 +81,7 @@ jobs:
|
||||
needs:
|
||||
- setup
|
||||
- create_next_release_branch_grafana
|
||||
uses: ./.github/workflows/migrate-prs.yml
|
||||
uses: grafana/grafana/.github/workflows/migrate-prs.yml@main
|
||||
with:
|
||||
ownerRepo: 'grafana/grafana'
|
||||
from: ${{ needs.setup.outputs.release_branch }}
|
||||
@@ -97,14 +90,14 @@ jobs:
|
||||
needs:
|
||||
- setup
|
||||
- create_next_release_branch_enterprise
|
||||
uses: ./.github/workflows/migrate-prs.yml
|
||||
uses: grafana/grafana/.github/workflows/migrate-prs.yml@main
|
||||
with:
|
||||
ownerRepo: 'grafana/grafana-enterprise'
|
||||
from: ${{ needs.setup.outputs.release_branch }}
|
||||
to: ${{ needs.create_next_release_branch_enterprise.outputs.branch }}
|
||||
post_changelog_on_forum:
|
||||
needs: setup
|
||||
uses: ./.github/workflows/community-release.yml
|
||||
uses: grafana/grafana/.github/workflows/community-release.yml@main
|
||||
with:
|
||||
version: ${{ needs.setup.outputs.version }}
|
||||
dry_run: ${{ needs.setup.outputs.dry_run == 'true' }}
|
||||
@@ -113,7 +106,7 @@ jobs:
|
||||
# The github-release action retrieves the changelog using the /repos/grafana/grafana/contents/CHANGELOG.md API
|
||||
# endpoint.
|
||||
needs: setup
|
||||
uses: ./.github/workflows/github-release.yml
|
||||
uses: grafana/grafana/.github/workflows/github-release.yml@main
|
||||
with:
|
||||
version: ${{ needs.setup.outputs.version }}
|
||||
dry_run: ${{ needs.setup.outputs.dry_run == 'true' }}
|
||||
|
||||
@@ -16,10 +16,6 @@ on:
|
||||
required: true
|
||||
type: string
|
||||
description: The version of Grafana that is being released (without the `v` prefix)`
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
description: The release branch pattern (eg v9.5.x) that these changes are being merged into
|
||||
changelog:
|
||||
required: false
|
||||
type: boolean
|
||||
@@ -73,19 +69,28 @@ jobs:
|
||||
id: vault-secrets
|
||||
uses: grafana/shared-workflows/actions/get-vault-secrets@main
|
||||
with:
|
||||
# Secrets placed in the ci/data/repo/grafana/grafana/delivery-bot-app path in Vault
|
||||
repo_secrets: |
|
||||
GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY
|
||||
- name: Generate token
|
||||
id: generate_changelog_token
|
||||
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
|
||||
with:
|
||||
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
|
||||
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
|
||||
repositories: "[\"grafana\", \"grafana-enterprise\"]"
|
||||
permissions: "{\"contents\": \"write\", \"pull_requests\": \"write\", \"workflows\":\"write\"}"
|
||||
- run: echo "RELEASE_BRANCH=release-${VERSION}" >> "$GITHUB_ENV"
|
||||
- name: Checkout Grafana
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ steps.generate_changelog_token.outputs.token }}
|
||||
ref: ${{ env.RELEASE_BRANCH }}
|
||||
fetch-tags: true
|
||||
fetch-depth: 0
|
||||
- name: Checkout Grafana (main)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ steps.generate_changelog_token.outputs.token }}
|
||||
ref: main
|
||||
fetch-depth: '0'
|
||||
path: .grafana-main
|
||||
@@ -94,21 +99,17 @@ jobs:
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .nvmrc
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
- name: Configure git user
|
||||
run: |
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "grafana-delivery-bot[bot]"
|
||||
git config --local user.email "grafana-delivery-bot[bot]@users.noreply.github.com"
|
||||
git config --local --add --bool push.autoSetupRemote true
|
||||
|
||||
- name: Create branch
|
||||
run: git checkout -b "release/${{ github.run_number }}/$VERSION"
|
||||
- name: Generate changelog token
|
||||
if: ${{ inputs.changelog == true || inputs.changelog == 'true' }}
|
||||
id: generate_changelog_token
|
||||
uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92
|
||||
with:
|
||||
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
|
||||
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
|
||||
- name: Generate changelog
|
||||
id: changelog
|
||||
if: ${{ inputs.changelog == true || inputs.changelog == 'true' }}
|
||||
@@ -156,13 +157,16 @@ jobs:
|
||||
- name: Commit CHANGELOG.md changes
|
||||
if: ${{ inputs.changelog == true || inputs.changelog == 'true' }}
|
||||
run: git add CHANGELOG.md && git commit --allow-empty -m "Update changelog" CHANGELOG.md
|
||||
|
||||
- name: Update package.json versions
|
||||
- name: Bump versions
|
||||
if: ${{ inputs.bump == true || inputs.bump == 'true' }}
|
||||
uses: ./.grafana-main/pkg/build/actions/bump-version
|
||||
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
|
||||
with:
|
||||
version: 'patch'
|
||||
verb: run
|
||||
args: go run -C .grafana-main ./pkg/build/actions/bump-version -version="patch"
|
||||
|
||||
- name: make gen-cue
|
||||
shell: bash
|
||||
run: make gen-cue
|
||||
- name: Add package.json changes
|
||||
if: ${{ inputs.bump == true || inputs.bump == 'true' }}
|
||||
run: |
|
||||
@@ -171,14 +175,13 @@ jobs:
|
||||
git commit -m "Update version to $VERSION"
|
||||
|
||||
- name: Git push
|
||||
if: inputs.dry_run != true
|
||||
run: git push
|
||||
- name: Create PR
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BRANCH: ${{ steps.branch.outputs.branch }}
|
||||
DRY_RUN: ${{ inputs.dry_run }}
|
||||
run: |
|
||||
LATEST_FLAG=""
|
||||
LATEST_FLAG=()
|
||||
if [ "$LATEST" = "true" ]; then
|
||||
LATEST_FLAG=(-l "release/latest")
|
||||
fi
|
||||
@@ -186,6 +189,6 @@ jobs:
|
||||
"${LATEST_FLAG[@]}" \
|
||||
-l "no-changelog" \
|
||||
--dry-run="$DRY_RUN" \
|
||||
-B "$BRANCH" \
|
||||
-B "${RELEASE_BRANCH}" \
|
||||
--title "Release: $VERSION" \
|
||||
--body "These code changes must be merged after a release is complete"
|
||||
|
||||
@@ -41,8 +41,6 @@ __debug_bin*
|
||||
# This is the new place of the block, but I leave the previous here for a while
|
||||
/devenv/docker/blocks/auth/saml-enterprise
|
||||
/devenv/docker/blocks/auth/signer
|
||||
/devenv/docker/blocks/spanner_tests
|
||||
/devenv/docker/blocks/spanner_tests_multi
|
||||
/devenv/docker/blocks/mt-db
|
||||
|
||||
/tmp
|
||||
|
||||
@@ -19,4 +19,3 @@
|
||||
# This is the new place of the block, but I leave the previous here for a while
|
||||
!/devenv/docker/blocks/auth/saml-enterprise
|
||||
!/devenv/docker/blocks/auth/signer
|
||||
!/devenv/docker/blocks/spanner_tests
|
||||
+179
@@ -1,3 +1,182 @@
|
||||
<!-- 12.0.1 START -->
|
||||
|
||||
# 12.0.1 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105101](https://github.com/grafana/grafana/pull/105101), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga from v1.8.6 to v1.8.12 [#105368](https://github.com/grafana/grafana/pull/105368), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.126.0 to v0.132.0 [#105249](https://github.com/grafana/grafana/pull/105249), [@macabu](https://github.com/macabu)
|
||||
- **K8s:** Dashboards: Add fine grained access control checks to /apis [#104419](https://github.com/grafana/grafana/pull/104419), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Dashboard:** Fix Panel Explore link subpath duplication (#104952) [#105056](https://github.com/grafana/grafana/pull/105056), [@axelavargas](https://github.com/axelavargas)
|
||||
- **Dashboard:** Fixes issue with row repeats and first row [#104469](https://github.com/grafana/grafana/pull/104469), [@torkelo](https://github.com/torkelo)
|
||||
- **Graphite:** Ensure template variables are interpolated correctly [#105389](https://github.com/grafana/grafana/pull/105389), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Graphite:** Fix Graphite series interpolation [#104516](https://github.com/grafana/grafana/pull/104516), [@aangelisc](https://github.com/aangelisc)
|
||||
- **InfluxDB:** Fix nested variable interpolation [#104176](https://github.com/grafana/grafana/pull/104176), [@aangelisc](https://github.com/aangelisc)
|
||||
- **MetricsDrilldown:** Restore link to Metrics Drilldown from Explore [#104073](https://github.com/grafana/grafana/pull/104073), [@NWRichmond](https://github.com/NWRichmond)
|
||||
- **NestedFolderPicker:** Fix scroll jumps back to top [#105769](https://github.com/grafana/grafana/pull/105769), [@samsch](https://github.com/samsch)
|
||||
- **Preferences:** Disable the save button whilst saving preferences [#105612](https://github.com/grafana/grafana/pull/105612), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **Prometheus:** Fix semver import path [#104945](https://github.com/grafana/grafana/pull/104945), [@jackw](https://github.com/jackw)
|
||||
- **Themes:** Prevent duplicated API call in drawer [#105611](https://github.com/grafana/grafana/pull/105611), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **XYChart:** Coerce threshold steps to numbers [#104492](https://github.com/grafana/grafana/pull/104492), [@leeoniya](https://github.com/leeoniya)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 12.0.1 END -->
|
||||
<!-- 11.6.2 START -->
|
||||
|
||||
# 11.6.2 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105103](https://github.com/grafana/grafana/pull/105103), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.4-git to v2.5.0 [#105443](https://github.com/grafana/grafana/pull/105443), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga from v1.8.6 to v1.8.12 [#105369](https://github.com/grafana/grafana/pull/105369), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.126.0 to v0.132.0 [#105251](https://github.com/grafana/grafana/pull/105251), [@macabu](https://github.com/macabu)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Dashboard:** Fixes issue with row repeats and first row [#104467](https://github.com/grafana/grafana/pull/104467), [@torkelo](https://github.com/torkelo)
|
||||
- **Graphite:** Ensure template variables are interpolated correctly [#105388](https://github.com/grafana/grafana/pull/105388), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Graphite:** Fix Graphite series interpolation [#104568](https://github.com/grafana/grafana/pull/104568), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Prometheus:** Fix semver import path [#104943](https://github.com/grafana/grafana/pull/104943), [@jackw](https://github.com/jackw)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 11.6.2 END -->
|
||||
<!-- 11.5.5 START -->
|
||||
|
||||
# 11.5.5 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105109](https://github.com/grafana/grafana/pull/105109), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.3 to v2.5.0 [#105441](https://github.com/grafana/grafana/pull/105441), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105373](https://github.com/grafana/grafana/pull/105373), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.126.0 to v0.132.0 [#105252](https://github.com/grafana/grafana/pull/105252), [@macabu](https://github.com/macabu)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 11.5.5 END -->
|
||||
<!-- 11.4.5 START -->
|
||||
|
||||
# 11.4.5 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105110](https://github.com/grafana/grafana/pull/105110), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.2 to v2.5.0 [#105445](https://github.com/grafana/grafana/pull/105445), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105375](https://github.com/grafana/grafana/pull/105375), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.125.0 to v0.132.0 [#105253](https://github.com/grafana/grafana/pull/105253), [@macabu](https://github.com/macabu)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 11.4.5 END -->
|
||||
<!-- 11.3.7 START -->
|
||||
|
||||
# 11.3.7 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105112](https://github.com/grafana/grafana/pull/105112), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/blevesearch/bleve/v2 from v2.4.2 to v2.5.0 [#105447](https://github.com/grafana/grafana/pull/105447), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105376](https://github.com/grafana/grafana/pull/105376), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.125.0 to v0.132.0 [#105254](https://github.com/grafana/grafana/pull/105254), [@macabu](https://github.com/macabu)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 11.3.7 END -->
|
||||
<!-- 11.2.10 START -->
|
||||
|
||||
# 11.2.10 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105113](https://github.com/grafana/grafana/pull/105113), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga from v1.8.5 to v1.8.12 [#105378](https://github.com/grafana/grafana/pull/105378), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Unpin and bump github.com/getkin/kin-openapi from v0.125.0 to v0.132.0 [#105255](https://github.com/grafana/grafana/pull/105255), [@macabu](https://github.com/macabu)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 11.2.10 END -->
|
||||
<!-- 10.4.19 START -->
|
||||
|
||||
# 10.4.19 (2025-05-22)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Chore:** Bump Go version to 1.24.3 [#105115](https://github.com/grafana/grafana/pull/105115), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump golang.org/x/net from v0.37.0 to v0.40.0 [#105449](https://github.com/grafana/grafana/pull/105449), [@macabu](https://github.com/macabu)
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
- **Security:** Fix CVE-2025-3580
|
||||
|
||||
<!-- 10.4.19 END -->
|
||||
<!-- 12.0.0+security-01 START -->
|
||||
|
||||
# 12.0.0+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 12.0.0+security-01 END -->
|
||||
<!-- 11.6.1+security-01 START -->
|
||||
|
||||
# 11.6.1+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 11.6.1+security-01 END -->
|
||||
<!-- 11.5.4+security-01 START -->
|
||||
|
||||
# 11.5.4+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 11.5.4+security-01 END -->
|
||||
<!-- 11.4.4+security-01 START -->
|
||||
|
||||
# 11.4.4+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 11.4.4+security-01 END -->
|
||||
<!-- 11.3.6+security-01 START -->
|
||||
|
||||
# 11.3.6+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 11.3.6+security-01 END -->
|
||||
<!-- 11.2.9+security-01 START -->
|
||||
|
||||
# 11.2.9+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 11.2.9+security-01 END -->
|
||||
<!-- 10.4.18+security-01 START -->
|
||||
|
||||
# 10.4.18+security-01 (2025-05-21)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fix CVE-2025-4123
|
||||
|
||||
<!-- 10.4.18+security-01 END -->
|
||||
<!-- 12.0.0 START -->
|
||||
|
||||
# 12.0.0 (2025-05-05)
|
||||
|
||||
@@ -30,7 +30,7 @@ func (c *check) ID() string {
|
||||
}
|
||||
|
||||
func (c *check) Name() string {
|
||||
return "SSO Setting"
|
||||
return "SSO setting"
|
||||
}
|
||||
|
||||
func (c *check) Init(ctx context.Context) error {
|
||||
|
||||
@@ -88,7 +88,7 @@ func (c *check) ID() string {
|
||||
}
|
||||
|
||||
func (c *check) Name() string {
|
||||
return "Data Source"
|
||||
return "data source"
|
||||
}
|
||||
|
||||
func (c *check) Init(ctx context.Context) error {
|
||||
|
||||
@@ -46,7 +46,7 @@ func (c *check) ID() string {
|
||||
}
|
||||
|
||||
func (c *check) Name() string {
|
||||
return "Plugin"
|
||||
return "plugin"
|
||||
}
|
||||
|
||||
func (c *check) Items(ctx context.Context) ([]any, error) {
|
||||
@@ -149,7 +149,7 @@ func (s *deprecationStep) Run(ctx context.Context, log logging.Logger, _ *adviso
|
||||
p.ID,
|
||||
[]advisor.CheckErrorLink{
|
||||
{
|
||||
Message: "Admin",
|
||||
Message: "View plugin",
|
||||
Url: fmt.Sprintf("/plugins/%s", p.ID),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestRun(t *testing.T) {
|
||||
Links: []advisor.CheckErrorLink{
|
||||
{
|
||||
Url: "/plugins/plugin1",
|
||||
Message: "Admin",
|
||||
Message: "View plugin",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -72,9 +72,12 @@ func (r *Runner) createOrUpdate(ctx context.Context, log logging.Logger, obj res
|
||||
return err
|
||||
}
|
||||
currentAnnotations := current.GetAnnotations()
|
||||
if currentAnnotations == nil {
|
||||
currentAnnotations = make(map[string]string)
|
||||
}
|
||||
annotations := obj.GetAnnotations()
|
||||
maps.Copy(annotations, currentAnnotations)
|
||||
obj.SetAnnotations(annotations)
|
||||
maps.Copy(currentAnnotations, annotations)
|
||||
obj.SetAnnotations(currentAnnotations) // This will update the annotations in the object
|
||||
_, err = r.client.Update(ctx, id, obj, resource.UpdateOptions{})
|
||||
if err != nil {
|
||||
// Ignore the error, it's probably due to a race condition
|
||||
|
||||
@@ -874,6 +874,7 @@ CustomVariableKind: {
|
||||
GroupByVariableSpec: {
|
||||
name: string | *""
|
||||
datasource?: DataSourceRef
|
||||
defaultValue?: VariableOption
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
|
||||
@@ -878,6 +878,7 @@ CustomVariableKind: {
|
||||
GroupByVariableSpec: {
|
||||
name: string | *""
|
||||
datasource?: DataSourceRef
|
||||
defaultValue?: VariableOption
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
|
||||
@@ -1601,15 +1601,16 @@ func NewDashboardGroupByVariableKind() *DashboardGroupByVariableKind {
|
||||
// GroupBy variable specification
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardGroupByVariableSpec struct {
|
||||
Name string `json:"name"`
|
||||
Datasource *DashboardDataSourceRef `json:"datasource,omitempty"`
|
||||
Current DashboardVariableOption `json:"current"`
|
||||
Options []DashboardVariableOption `json:"options"`
|
||||
Multi bool `json:"multi"`
|
||||
Label *string `json:"label,omitempty"`
|
||||
Hide DashboardVariableHide `json:"hide"`
|
||||
SkipUrlSync bool `json:"skipUrlSync"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Datasource *DashboardDataSourceRef `json:"datasource,omitempty"`
|
||||
DefaultValue *DashboardVariableOption `json:"defaultValue,omitempty"`
|
||||
Current DashboardVariableOption `json:"current"`
|
||||
Options []DashboardVariableOption `json:"options"`
|
||||
Multi bool `json:"multi"`
|
||||
Label *string `json:"label,omitempty"`
|
||||
Hide DashboardVariableHide `json:"hide"`
|
||||
SkipUrlSync bool `json:"skipUrlSync"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
}
|
||||
|
||||
// NewDashboardGroupByVariableSpec creates a new DashboardGroupByVariableSpec object.
|
||||
|
||||
@@ -112,22 +112,36 @@ Content-Type: application/json
|
||||
Content-Length: 357
|
||||
|
||||
{
|
||||
"jti":"2",
|
||||
"iss":"https://grafana.com",
|
||||
"sub":"https://play.grafana.org/"
|
||||
"lid":"1",
|
||||
"included_users":15,
|
||||
"lic_exp_warn_days":30,
|
||||
"tok_exp_warn_days":2,
|
||||
"update_days":1,
|
||||
"prod":["grafana-enterprise"],
|
||||
"company":"Grafana Labs"
|
||||
"status":0,
|
||||
"jti":"",
|
||||
"iss":"",
|
||||
"sub":"",
|
||||
"iat":0,
|
||||
"exp":0,
|
||||
"nbf":0,
|
||||
"lexp":0,
|
||||
"lid":"",
|
||||
"limit_by":"",
|
||||
"included_users":0,
|
||||
"lic_exp_warn_days":0,
|
||||
"tok_exp_warn_days":0,
|
||||
"update_days":0,
|
||||
"prod":null,
|
||||
"company":"",
|
||||
"account":"",
|
||||
"slug":"",
|
||||
"usage_billing":false,
|
||||
"max_concurrent_user_sessions":0,
|
||||
"details_url":"",
|
||||
"trial":false,
|
||||
"trial_exp":0,
|
||||
"anonymousRatio":0
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The response is a JSON blob available for debugging purposes. The
|
||||
available fields may change at any time without any prior notice.
|
||||
The response is a JSON blob with specific values intentionally not shown. The
|
||||
available fields may change at any time without any prior notice. Refer to [Check license availability](#check-license-availability) for information on using the API to check the status of your license.
|
||||
|
||||
Status Codes:
|
||||
|
||||
|
||||
@@ -147,3 +147,54 @@ Status Codes:
|
||||
- **401** - Unauthorized
|
||||
- **403** - Permission denied
|
||||
- **404** - Team not found/Group not found
|
||||
|
||||
## Search Team Groups
|
||||
|
||||
`GET /api/teams/:teamId/groups/search`
|
||||
|
||||
Search for team groups with pagination support.
|
||||
|
||||
**Required permissions**
|
||||
|
||||
| Action | Scope |
|
||||
| ---------------------- | -------- |
|
||||
| teams.permissions:read | teams:\* |
|
||||
|
||||
**Example Request**:
|
||||
|
||||
```http
|
||||
GET /api/teams/1/groups/search?name=editors&query=group&page=1&perpage=10 HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer glsa_kcVxDhZtu5ISOZIEt
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
|
||||
[
|
||||
{
|
||||
"totalCount": 1,
|
||||
"teamGroups": [
|
||||
{
|
||||
"orgId": 1,
|
||||
"teamId": 1,
|
||||
"groupId": "cn=editors,ou=groups,dc=grafana,dc=org"
|
||||
}
|
||||
],
|
||||
"page": 1,
|
||||
"perPage": 10
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Status Codes:
|
||||
|
||||
- **200** - Ok
|
||||
- **400** - Bad Request (invalid team ID format or missing query parameter)
|
||||
- **401** - Unauthorized
|
||||
- **403** - Permission denied
|
||||
- **500** - Internal Server Error
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
"grafanaDependency": {
|
||||
"type": "string",
|
||||
"description": "Required Grafana version for this plugin. Validated using https://github.com/npm/node-semver.",
|
||||
"pattern": "^(<=|>=|<|>|=|~|\\^)?([0-9]+)(\\.[0-9x\\*]+)?(\\.[0-9x\\*]+)?(\\s(<=|>=|<|=>)?([0-9]+)(\\.[0-9x\\*]+)?(\\.[0-9x\\*]+)?)?$"
|
||||
"pattern": "^(<=|>=|<|>|=|~|\\^)?([0-9]+)(\\.[0-9x\\*]+)?(\\.[0-9x\\*]+)?(-[0-9A-Za-z-.]+)?(\\s(<=|>=|<|=>)?([0-9]+)(\\.[0-9x\\*]+)?(\\.[0-9x\\*]+)?(-[0-9A-Za-z-.]+)?)?$"
|
||||
},
|
||||
"plugins": {
|
||||
"type": "array",
|
||||
|
||||
@@ -18,7 +18,7 @@ weight: 300
|
||||
# Provision resources and sync dashboards
|
||||
|
||||
{{< admonition type="caution" >}}
|
||||
Provisioning is an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. This feature is not publicly available in Grafana Cloud yet.
|
||||
Provisioning is an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. This feature is not publicly available in Grafana Cloud yet. Only the cloud-hosted version of GitHub (GitHub.com) is supported at this time. GitHub Enterprise is not yet compatible.
|
||||
|
||||
Sign up for Grafana Cloud Git Sync early access using [this form](https://forms.gle/WKkR3EVMcbqsNnkD9).
|
||||
{{< /admonition >}}
|
||||
|
||||
@@ -16,7 +16,7 @@ weight: 200
|
||||
# Set up file provisioning
|
||||
|
||||
{{< admonition type="caution" >}}
|
||||
Local file provisioning is an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `provisioning` and `kubernetesDashboards` feature toggles in Grafana to use this feature. This feature is not publicly available in Grafana Cloud yet.
|
||||
Local file provisioning is an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `provisioning` and `kubernetesDashboards` feature toggles in Grafana to use this feature. This feature is not publicly available in Grafana Cloud yet. Only the cloud-hosted version of GitHub (GitHub.com) is supported at this time. GitHub Enterprise is not yet compatible.
|
||||
|
||||
Sign up for Grafana Cloud Git Sync early access using [this form](https://forms.gle/WKkR3EVMcbqsNnkD9).
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ weight: 100
|
||||
# Set up Git Sync
|
||||
|
||||
{{< admonition type="caution" >}}
|
||||
Git Sync is an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `provisioning` and `kubernetesDashboards` feature toggles in Grafana to use this feature. This feature is not publicly available in Grafana Cloud yet.
|
||||
Git Sync is an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `provisioning` and `kubernetesDashboards` feature toggles in Grafana to use this feature. This feature is not publicly available in Grafana Cloud yet. Only the cloud-hosted version of GitHub (GitHub.com) is supported at this time. GitHub Enterprise is not yet compatible.
|
||||
|
||||
Sign up for Grafana Cloud Git Sync early access using [this form](https://forms.gle/WKkR3EVMcbqsNnkD9).
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ weight: 100
|
||||
# Git Sync
|
||||
|
||||
{{< admonition type="caution" >}}
|
||||
Git Sync is an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `provisioning` and `kubernetesDashboards` feature toggles in Grafana to use this feature. This feature is not publicly available in Grafana Cloud yet.
|
||||
Git Sync is an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `provisioning` and `kubernetesDashboards` feature toggles in Grafana to use this feature. This feature is not publicly available in Grafana Cloud yet. Only the cloud-hosted version of GitHub (GitHub.com) is supported at this time. GitHub Enterprise is not yet compatible.
|
||||
|
||||
Sign up for Grafana Cloud Git Sync early access using [this form](https://forms.gle/WKkR3EVMcbqsNnkD9).
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ weight: 300
|
||||
# Work with provisioned dashboards
|
||||
|
||||
{{< admonition type="caution" >}}
|
||||
Git Sync and File path provisioning an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `provisioning` and `kubernetesDashboards` feature toggles in Grafana. These features aren't available publicly in Grafana Cloud yet.
|
||||
Git Sync and File path provisioning an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `provisioning` and `kubernetesDashboards` feature toggles in Grafana. These features aren't available publicly in Grafana Cloud yet. Only the cloud-hosted version of GitHub (GitHub.com) is supported at this time. GitHub Enterprise is not yet compatible.
|
||||
|
||||
Sign up for Grafana Cloud Git Sync early access using [this form](https://forms.gle/WKkR3EVMcbqsNnkD9).
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ weight: 400
|
||||
# Manage provisioned repositories with Git Sync
|
||||
|
||||
{{< admonition type="caution" >}}
|
||||
Git Sync is an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `provisioning` and `kubernetesDashboards` feature toggles in Grafana to use this feature. This feature is not publicly available in Grafana Cloud yet.
|
||||
Git Sync is an [experimental feature](https://grafana.com/docs/release-life-cycle/) introduced in Grafana v12 for open source and Enterprise editions. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. Enable the `provisioning` and `kubernetesDashboards` feature toggles in Grafana to use this feature. This feature is not publicly available in Grafana Cloud yet. Only the cloud-hosted version of GitHub (GitHub.com) is supported at this time. GitHub Enterprise is not yet compatible.
|
||||
|
||||
Sign up for Grafana Cloud Git Sync early access using [this form](https://forms.gle/WKkR3EVMcbqsNnkD9).
|
||||
|
||||
|
||||
@@ -148,31 +148,31 @@ The table includes default and other fields:
|
||||
### `annotations`
|
||||
|
||||
The configuration for the list of annotations that are associated with the dashboard.
|
||||
For the JSON and field usage notes, refer to the [annotations schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/annotations-schema/).
|
||||
For the JSON and field usage notes, refer to the [annotations schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/annotations-schema/).
|
||||
|
||||
### `elements`
|
||||
|
||||
Dashboards can contain the following elements:
|
||||
|
||||
- [PanelKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/panel-schema/)
|
||||
- [LibraryPanelKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/librarypanel-schema/)
|
||||
- [PanelKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/panel-schema/)
|
||||
- [LibraryPanelKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/librarypanel-schema/)
|
||||
|
||||
### `layout`
|
||||
|
||||
Dashboards can have four layout options:
|
||||
|
||||
- [GridLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/layout-schema/#gridlayoutkind)
|
||||
- [AutoGridLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/layout-schema/#autogridlayoutkind)
|
||||
- [RowsLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/layout-schema/#rowslayoutkind)
|
||||
- [TabsLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/layout-schema/#tabslayoutkind)
|
||||
- [GridLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/layout-schema/#gridlayoutkind)
|
||||
- [AutoGridLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/layout-schema/#autogridlayoutkind)
|
||||
- [RowsLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/layout-schema/#rowslayoutkind)
|
||||
- [TabsLayoutKind](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/layout-schema/#tabslayoutkind)
|
||||
|
||||
For the JSON and field usage notes about each of these, refer to the [layout schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/layout-schema/).
|
||||
For the JSON and field usage notes about each of these, refer to the [layout schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/layout-schema/).
|
||||
|
||||
### `links`
|
||||
|
||||
The configuration for links with references to other dashboards or external websites.
|
||||
|
||||
For the JSON and field usage notes, refer to the [links schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/links-schema/).
|
||||
For the JSON and field usage notes, refer to the [links schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/links-schema/).
|
||||
|
||||
### `tags`
|
||||
|
||||
@@ -183,7 +183,7 @@ The tags associated with the dashboard:
|
||||
### `timesettings`
|
||||
|
||||
The `TimeSettingsSpec` defines the default time configuration for the time picker and the refresh picker for the specific dashboard.
|
||||
For the JSON and field usage notes about the `TimeSettingsSpec`, refer to the [timesettings schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/timesettings-schema/).
|
||||
For the JSON and field usage notes about the `TimeSettingsSpec`, refer to the [timesettings schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/timesettings-schema/).
|
||||
|
||||
### `variables`
|
||||
|
||||
@@ -200,7 +200,7 @@ There are eight variables types:
|
||||
- GroupByVariableKind
|
||||
- AdhocVariableKind
|
||||
|
||||
For the JSON and field usage notes about the `variables` spec, refer to the [variables schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/variables-schema/).
|
||||
For the JSON and field usage notes about the `variables` spec, refer to the [variables schema documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/variables-schema/).
|
||||
|
||||
## Notes and limitations
|
||||
|
||||
|
||||
@@ -87,14 +87,14 @@ Following is the JSON for a default grid layout, a grid layout item, and a grid
|
||||
|
||||
The following table explains the usage of the grid layout item JSON fields:
|
||||
|
||||
| Name | Usage |
|
||||
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| x | integer. Position of the item x-axis. |
|
||||
| y | integer. Position of the item y-axis. |
|
||||
| width | Width of the item in pixels. |
|
||||
| height | Height of the item in pixels. |
|
||||
| element | `ElementReference`. Reference to a [`PanelKind`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/panel-schema/) from `dashboard.spec.elements` expressed as JSON Schema reference. |
|
||||
| repeat? | [RepeatOptions](#repeatoptions). Configured repeat options, if any |
|
||||
| Name | Usage |
|
||||
| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| x | integer. Position of the item x-axis. |
|
||||
| y | integer. Position of the item y-axis. |
|
||||
| width | Width of the item in pixels. |
|
||||
| height | Height of the item in pixels. |
|
||||
| element | `ElementReference`. Reference to a [`PanelKind`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/panel-schema/) from `dashboard.spec.elements` expressed as JSON Schema reference. |
|
||||
| repeat? | [RepeatOptions](#repeatoptions). Configured repeat options, if any |
|
||||
|
||||
#### `RepeatOptions`
|
||||
|
||||
@@ -188,7 +188,7 @@ The following table explains the usage of the auto grid layout item JSON fields:
|
||||
|
||||
| Name | Usage |
|
||||
| ---- | ----- |
|
||||
| element | `ElementReference`. Reference to a [`PanelKind`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/json-models/schema-v2/panel-schema/) from `dashboard.spec.elements` expressed as JSON Schema reference. |
|
||||
| element | `ElementReference`. Reference to a [`PanelKind`](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/observability-as-code/schema-v2/panel-schema/) from `dashboard.spec.elements` expressed as JSON Schema reference. |
|
||||
| repeat? | [AutoGridRepeatOptions](#autogridrepeatoptions). Configured repeat options, if any. |
|
||||
| conditionalRendering? | `ConditionalRenderingGroupKind`. Rules for hiding or showing panels, if any. Consists of:<ul><li>kind: "ConditionalRenderingGroup"</li><li>spec: [ConditionalRenderingGroupSpec](#conditionalrenderinggroupspec)</li></ul> |
|
||||
|
||||
|
||||
@@ -201,6 +201,7 @@ Use this transformation to add a new field calculated from two other fields. Eac
|
||||
- **All number fields** - Set the left side of a **Binary operation** to apply the calculation to all number fields.
|
||||
- **As percentile** - If you select **Row index** mode, then the **As percentile** switch appears. This switch allows you to transform the row index as a percentage of the total number of rows.
|
||||
- **Alias** - (Optional) Enter the name of your new field. If you leave this blank, then the field will be named to match the calculation.
|
||||
> **Note:** If a variable will be used in this transformation, the default alias will be interpolated with the value of the variable. Please explicitly define an alias if you would like the alias to not be affected by variable changes.
|
||||
- **Replace all fields** - (Optional) Select this option if you want to hide all other fields and display only your calculated field in the visualization.
|
||||
|
||||
In the example below, we added two fields together and named them Sum.
|
||||
|
||||
+4
@@ -209,6 +209,10 @@ You can use the `org_attribute_path` and `org_mapping` configuration options to
|
||||
|
||||
To ease configuration of a proper JMESPath expression, go to [JMESPath](http://jmespath.org/) to test and evaluate expressions with custom payloads.
|
||||
|
||||
{{< admonition type="note" >}}
|
||||
When using `org_attribute_path`, the value returned by the JMESPath expression must be an array, not a string.
|
||||
{{< /admonition >}}
|
||||
|
||||
#### Role mapping examples
|
||||
|
||||
This section includes examples of JMESPath expressions used for role mapping.
|
||||
|
||||
@@ -85,7 +85,7 @@ The table below describes all SCIM configuration options. Like any other Grafana
|
||||
|
||||
- SCIM group sync (`group_sync_enabled = true`) and Team Sync cannot be enabled simultaneously
|
||||
- You can use SCIM user sync (`user_sync_enabled = true`) alongside Team Sync
|
||||
- For more details about migration and compatibility, see [SCIM vs Team Sync](./manage-users-teams/_index.md#scim-vs-team-sync)
|
||||
- For more details about migration and compatibility, see [SCIM vs Team Sync](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-scim-provisioning/manage-users-teams/#scim-vs-team-sync)
|
||||
{{< /admonition >}}
|
||||
|
||||
### Example SCIM configuration
|
||||
@@ -134,5 +134,5 @@ The following table compares SCIM with other synchronization methods to help you
|
||||
## Next steps
|
||||
|
||||
- [Manage users and teams with SCIM provisioning](manage-users-teams/)
|
||||
- [Configure SCIM with Azure AD](azuread/)
|
||||
- [Configure SCIM with Okta](okta/)
|
||||
- [Configure SCIM with Azure AD](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-scim-provisioning/configure-scim-with-azuread/)
|
||||
- [Configure SCIM with Okta](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-scim-provisioning/configure-scim-with-okta/)
|
||||
|
||||
@@ -128,7 +128,7 @@ services:
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TERM=linux
|
||||
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-polystat-panel
|
||||
- GF_PLUGINS_PREINSTALL=grafana-clock-panel,grafana-polystat-panel
|
||||
ports:
|
||||
- '3000:3000'
|
||||
volumes:
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { e2e } from '../utils';
|
||||
|
||||
import { flows } from './dashboard-edit-flows';
|
||||
|
||||
describe('Dashboard panels', () => {
|
||||
beforeEach(() => {
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
|
||||
});
|
||||
|
||||
it('can duplicate a panel', () => {
|
||||
e2e.flows.scenes.importV2Dashboard({ title: 'Paste tab' });
|
||||
|
||||
e2e.flows.scenes.toggleEditMode();
|
||||
const panelTitle = 'Unique';
|
||||
flows.changePanelTitle('New panel', panelTitle);
|
||||
|
||||
e2e.components.Panels.Panel.title(panelTitle).should('have.length', 1);
|
||||
|
||||
e2e.components.Panels.Panel.menu(panelTitle).click({ force: true });
|
||||
e2e.components.Panels.Panel.menuItems('More...').trigger('mouseover');
|
||||
e2e.components.Panels.Panel.menuItems('Duplicate').click();
|
||||
|
||||
e2e.components.Panels.Panel.title(panelTitle).should('have.length', 2);
|
||||
|
||||
// Save, reload, and ensure duplicate has persisted
|
||||
e2e.flows.scenes.saveDashboard();
|
||||
cy.reload();
|
||||
e2e.components.Panels.Panel.title(panelTitle).should('have.length', 2);
|
||||
});
|
||||
});
|
||||
@@ -34,6 +34,10 @@ describe('Dashboard edit - variables', () => {
|
||||
e2e.pages.Dashboard.SubMenu.submenuItemLabels(variable.label).should('be.visible').contains(variable.label);
|
||||
const values = variable.value.split(',');
|
||||
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts(values[0]).should('be.visible');
|
||||
|
||||
// check that variable deletion works
|
||||
e2e.components.EditPaneHeader.deleteButton().click();
|
||||
e2e.pages.Dashboard.SubMenu.submenuItemLabels(variable.label).should('not.exist');
|
||||
});
|
||||
|
||||
it('can add a new constant variable', () => {
|
||||
|
||||
@@ -6,7 +6,6 @@ require (
|
||||
buf.build/gen/go/parca-dev/parca/connectrpc/go v1.17.0-20240902100956-02fd72488966.1 // @grafana/observability-traces-and-profiling
|
||||
buf.build/gen/go/parca-dev/parca/protocolbuffers/go v1.34.2-20240902100956-02fd72488966.2 // @grafana/observability-traces-and-profiling
|
||||
cloud.google.com/go/kms v1.20.5 // @grafana/grafana-backend-group
|
||||
cloud.google.com/go/spanner v1.75.0 // @grafana/grafana-search-and-storage
|
||||
cloud.google.com/go/storage v1.50.0 // @grafana/grafana-backend-group
|
||||
connectrpc.com/connect v1.17.0 // @grafana/observability-traces-and-profiling
|
||||
cuelang.org/go v0.11.1 // @grafana/grafana-as-code
|
||||
@@ -75,7 +74,6 @@ require (
|
||||
github.com/google/uuid v1.6.0 // @grafana/grafana-backend-group
|
||||
github.com/google/wire v0.6.0 // @grafana/grafana-backend-group
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // @grafana/grafana-backend-group
|
||||
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.4-0.20250319132907-e064f32e3674 // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/alerting v0.0.0-20250521131632-6e476b0b04c3 // @grafana/alerting-backend
|
||||
@@ -193,7 +191,7 @@ require (
|
||||
gopkg.in/mail.v2 v2.3.1 // @grafana/grafana-backend-group
|
||||
gopkg.in/yaml.v2 v2.4.0 // @grafana/alerting-backend
|
||||
gopkg.in/yaml.v3 v3.0.1 // @grafana/alerting-backend
|
||||
k8s.io/api v0.32.3 // indirect; @grafana/grafana-app-platform-squad
|
||||
k8s.io/api v0.32.3 // @grafana/grafana-app-platform-squad
|
||||
k8s.io/apimachinery v0.32.3 // @grafana/grafana-app-platform-squad
|
||||
k8s.io/apiserver v0.32.3 // @grafana/grafana-app-platform-squad
|
||||
k8s.io/client-go v0.32.3 // @grafana/grafana-app-platform-squad
|
||||
@@ -202,6 +200,7 @@ require (
|
||||
k8s.io/kube-aggregator v0.32.0 // @grafana/grafana-app-platform-squad
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // @grafana/grafana-app-platform-squad
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // @grafana/partner-datasources
|
||||
sigs.k8s.io/randfill v1.0.0 // @grafana/grafana-app-platform-squad
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // @grafana-app-platform-squad
|
||||
xorm.io/builder v0.3.6 // @grafana/grafana-backend-group
|
||||
)
|
||||
@@ -252,7 +251,6 @@ require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
|
||||
github.com/FZambia/eagle v0.2.0 // indirect
|
||||
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect
|
||||
@@ -569,7 +567,6 @@ require (
|
||||
modernc.org/sqlite v1.37.0 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
|
||||
@@ -187,7 +187,6 @@ cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvj
|
||||
cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA=
|
||||
cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs=
|
||||
cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
|
||||
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
|
||||
cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI=
|
||||
cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
|
||||
@@ -541,8 +540,6 @@ cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+
|
||||
cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos=
|
||||
cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk=
|
||||
cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M=
|
||||
cloud.google.com/go/spanner v1.75.0 h1:2zrltTJv/4P3pCgpYgde4Eb1vN8Cgy1fNy7pbTnOovg=
|
||||
cloud.google.com/go/spanner v1.75.0/go.mod h1:TLFZBvPQmx3We7sGh12eTk9lLsRLczzZaiweqfMpR80=
|
||||
cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM=
|
||||
cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ=
|
||||
cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0=
|
||||
@@ -718,8 +715,6 @@ github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/FZambia/eagle v0.2.0 h1:1kQaZpJvbkvAXFRE/9K2ucBMuVqo+E29EMLYB74hIis=
|
||||
github.com/FZambia/eagle v0.2.0/go.mod h1:LKMYBwGYhao5sJI0TppvQ4SvvldFj9gITxrl8NvGwG0=
|
||||
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2 h1:DBjmt6/otSdULyJdVg2BlG0qGZO5tKL4VzOs0jpvw5Q=
|
||||
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.2/go.mod h1:dppbR7CwXD4pgtV9t3wD1812RaLDcBjtblcDF5f1vI0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0/go.mod h1:2bIszWvQRlJVmJLiuLhukLImRjKPcYdzzsx6darK02A=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI=
|
||||
@@ -1019,7 +1014,6 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk=
|
||||
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
@@ -1133,7 +1127,6 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34=
|
||||
github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI=
|
||||
github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q=
|
||||
github.com/envoyproxy/go-control-plane v0.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
|
||||
github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
|
||||
@@ -1144,7 +1137,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
@@ -1549,8 +1541,6 @@ github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEP
|
||||
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/go-sql-spanner v1.11.1 h1:z3ThtKV5HFvaNv9UGc26+ggS+lS0dsCAkaFduKL7vws=
|
||||
github.com/googleapis/go-sql-spanner v1.11.1/go.mod h1:fuA5q4yMS3SZiVfRr5bvksPNk7zUn/irbQW62H/ffZw=
|
||||
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
||||
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
@@ -2392,7 +2382,6 @@ github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
@@ -3398,11 +3387,9 @@ google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD
|
||||
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
|
||||
google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
|
||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
|
||||
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
||||
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
|
||||
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
|
||||
+1
-2
@@ -442,6 +442,7 @@ cloud.google.com/go/shell v1.8.1/go.mod h1:jaU7OHeldDhTwgs3+clM0KYEDYnBAPevUI6wN
|
||||
cloud.google.com/go/shell v1.8.3 h1:mjYgUsOtV3jl9xvDmcvlRRmA64deEPf52zOfuc68b/g=
|
||||
cloud.google.com/go/shell v1.8.3/go.mod h1:OYcrgWF6JSp/uk76sNTtYFlMD0ho2+Cdzc7U3P/bF54=
|
||||
cloud.google.com/go/spanner v1.70.0/go.mod h1:X5T0XftydYp0K1adeJQDJtdWpbrOeJ7wHecM4tK6FiE=
|
||||
cloud.google.com/go/spanner v1.73.0 h1:0bab8QDn6MNj9lNK6XyGAVFhMlhMU2waePPa6GZNoi8=
|
||||
cloud.google.com/go/spanner v1.73.0/go.mod h1:mw98ua5ggQXVWwp83yjwggqEmW9t8rjs9Po1ohcUGW4=
|
||||
cloud.google.com/go/speech v1.25.1 h1:iGZJS3wrdkje/Vqiacx1+r+zVwUZoXVMdklYIVsvfNw=
|
||||
cloud.google.com/go/speech v1.25.1/go.mod h1:WgQghvghkZ1htG6BhYn98mP7Tg0mti8dBFDLMVXH/vM=
|
||||
@@ -1047,8 +1048,6 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg78
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
|
||||
+5
-5
@@ -259,7 +259,7 @@
|
||||
"yargs": "^17.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bsull/augurs": "^0.9.0",
|
||||
"@bsull/augurs": "^0.10.0",
|
||||
"@emotion/css": "11.13.5",
|
||||
"@emotion/react": "11.14.0",
|
||||
"@fingerprintjs/fingerprintjs": "^3.4.2",
|
||||
@@ -267,7 +267,7 @@
|
||||
"@formatjs/intl-durationformat": "^0.7.0",
|
||||
"@glideapps/glide-data-grid": "^6.0.0",
|
||||
"@grafana/alerting": "workspace:*",
|
||||
"@grafana/aws-sdk": "0.6.0",
|
||||
"@grafana/aws-sdk": "0.7.0",
|
||||
"@grafana/azure-sdk": "0.0.7",
|
||||
"@grafana/data": "workspace:*",
|
||||
"@grafana/e2e-selectors": "workspace:*",
|
||||
@@ -278,7 +278,7 @@
|
||||
"@grafana/google-sdk": "0.1.2",
|
||||
"@grafana/i18n": "workspace:*",
|
||||
"@grafana/lezer-logql": "0.2.7",
|
||||
"@grafana/llm": "0.13.2",
|
||||
"@grafana/llm": "0.19.2",
|
||||
"@grafana/monaco-logql": "^0.0.8",
|
||||
"@grafana/o11y-ds-frontend": "workspace:*",
|
||||
"@grafana/plugin-ui": "0.10.6",
|
||||
@@ -302,7 +302,7 @@
|
||||
"@msagl/parser": "^1.1.19",
|
||||
"@opentelemetry/api": "1.9.0",
|
||||
"@opentelemetry/exporter-collector": "0.25.0",
|
||||
"@opentelemetry/semantic-conventions": "1.28.0",
|
||||
"@opentelemetry/semantic-conventions": "1.34.0",
|
||||
"@popperjs/core": "2.11.8",
|
||||
"@react-aria/dialog": "3.5.25",
|
||||
"@react-aria/focus": "3.20.3",
|
||||
@@ -390,7 +390,7 @@
|
||||
"react-router": "5.3.4",
|
||||
"react-router-dom": "5.3.4",
|
||||
"react-router-dom-v5-compat": "^6.26.1",
|
||||
"react-select": "5.10.0",
|
||||
"react-select": "5.10.1",
|
||||
"react-split-pane": "0.1.92",
|
||||
"react-table": "7.8.0",
|
||||
"react-transition-group": "4.4.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isNumber, set, unset, get, cloneDeep } from 'lodash';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import usePrevious from 'react-use/lib/usePrevious';
|
||||
import { usePrevious } from 'react-use';
|
||||
|
||||
import { ThresholdsMode, VariableFormatID } from '@grafana/schema';
|
||||
|
||||
|
||||
@@ -814,7 +814,11 @@ export {
|
||||
export {
|
||||
type Action,
|
||||
type ActionModel,
|
||||
type ActionVariable,
|
||||
type ActionVariableInput,
|
||||
ActionType,
|
||||
HttpRequestMethod,
|
||||
ActionVariableType,
|
||||
defaultActionConfig,
|
||||
contentTypeOptions,
|
||||
httpMethodOptions,
|
||||
|
||||
@@ -695,9 +695,9 @@ export function getNameFromOptions(options: CalculateFieldTransformerOptions) {
|
||||
const { binary } = options;
|
||||
const alias = `${binary?.left?.matcher?.options ?? binary?.left?.fixed ?? ''} ${binary?.operator ?? ''} ${binary?.right?.matcher?.options ?? binary?.right?.fixed ?? ''}`;
|
||||
|
||||
//Remove $ signs as they will be interpolated and cause issues. Variables can still be used
|
||||
//in alias but shouldn't in the autogenerated name
|
||||
return alias.replace(/\$/g, '');
|
||||
// binary calculations with variables will be interpolated on the visualization but we don't want to do that here, so just give a blank placeholder
|
||||
const variableFound = /\$/g.test(alias);
|
||||
return variableFound ? '' : alias;
|
||||
}
|
||||
case CalculateFieldMode.ReduceRow:
|
||||
{
|
||||
|
||||
@@ -86,9 +86,17 @@ describe('Transpose transformer', () => {
|
||||
{ name: 'env', type: FieldType.string, values: ['dev', 'prod', 'staging', 'release', 'beta'] },
|
||||
{ name: 'january', type: FieldType.number, values: [11, 12, 13, 14, 15] },
|
||||
{ name: 'february', type: FieldType.number, values: [6, 7, 8, 9, 10] },
|
||||
{ name: 'type', type: FieldType.string, values: ['metricA', 'metricB', 'metricC', 'metricD', 'metricE'] },
|
||||
{
|
||||
name: 'metricName',
|
||||
type: FieldType.string,
|
||||
values: ['metricA', 'metricB', 'metricC', 'metricD', 'metricE'],
|
||||
config: {
|
||||
displayName: 'type',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await expect(transformDataFrame([cfgB], [seriesB])).toEmitValuesWith((received) => {
|
||||
const result = received[0];
|
||||
expect(result[0].fields).toEqual([
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { cacheFieldDisplayNames } from '../../field/fieldState';
|
||||
import { DataFrame, Field, FieldType } from '../../types/dataFrame';
|
||||
import { DataTransformerInfo } from '../../types/transformations';
|
||||
|
||||
@@ -28,6 +29,8 @@ export const transposeTransformer: DataTransformerInfo<TransposeTransformerOptio
|
||||
};
|
||||
|
||||
function transposeDataFrame(options: TransposeTransformerOptions, data: DataFrame[]): DataFrame[] {
|
||||
cacheFieldDisplayNames(data);
|
||||
|
||||
return data.map((frame) => {
|
||||
const firstField = frame.fields[0];
|
||||
const firstName = !options.firstFieldName ? 'Field' : options.firstFieldName;
|
||||
@@ -38,8 +41,12 @@ function transposeDataFrame(options: TransposeTransformerOptions, data: DataFram
|
||||
? [firstName, ...fieldValuesAsStrings(firstField, firstField.values)]
|
||||
: [firstName, ...firstField.values.map((_, i) => restName)];
|
||||
const rows = useFirstFieldAsHeaders
|
||||
? frame.fields.map((field) => field.name).slice(1)
|
||||
: frame.fields.map((field) => field.name);
|
||||
? frame.fields
|
||||
.map((field) => {
|
||||
return field.state?.displayName ?? field.name;
|
||||
})
|
||||
.slice(1)
|
||||
: frame.fields.map((field) => field.state?.displayName ?? field.name);
|
||||
const fieldType = determineFieldType(
|
||||
useFirstFieldAsHeaders
|
||||
? frame.fields.map((field) => field.type).slice(1)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CSSProperties } from 'react';
|
||||
import { CSSProperties, ReactNode } from 'react';
|
||||
|
||||
import { SelectableValue } from './select';
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface Action {
|
||||
[ActionType.Fetch]: FetchOptions;
|
||||
confirmation?: string;
|
||||
oneClick?: boolean;
|
||||
variables?: ActionVariable[];
|
||||
style?: ActionButtonCssProperties;
|
||||
}
|
||||
|
||||
@@ -26,10 +27,21 @@ export interface Action {
|
||||
*/
|
||||
export interface ActionModel<T = any> {
|
||||
title: string;
|
||||
onClick: (event: any, origin?: any) => void;
|
||||
confirmation?: string;
|
||||
onClick: (event: any, origin?: any, actionVars?: ActionVariableInput) => void;
|
||||
confirmation: (actionVars?: ActionVariableInput) => ReactNode;
|
||||
oneClick?: boolean;
|
||||
style: ActionButtonCssProperties;
|
||||
variables?: ActionVariable[];
|
||||
}
|
||||
|
||||
export type ActionVariable = {
|
||||
key: string;
|
||||
name: string;
|
||||
type: ActionVariableType;
|
||||
};
|
||||
|
||||
export enum ActionVariableType {
|
||||
String = 'string',
|
||||
}
|
||||
|
||||
interface FetchOptions {
|
||||
@@ -70,3 +82,5 @@ export const defaultActionConfig: Action = {
|
||||
headers: [['Content-Type', 'application/json']],
|
||||
},
|
||||
};
|
||||
|
||||
export type ActionVariableInput = { [key: string]: string };
|
||||
|
||||
@@ -679,10 +679,6 @@ export interface FeatureToggles {
|
||||
*/
|
||||
exploreLogsLimitedTimeRange?: boolean;
|
||||
/**
|
||||
* Used in Home for users who want to return to the onboarding flow or quickly find popular config pages
|
||||
*/
|
||||
homeSetupGuide?: boolean;
|
||||
/**
|
||||
* Enables the gRPC client to authenticate with the App Platform by using ID & access tokens
|
||||
*/
|
||||
appPlatformGrpcClientAuth?: boolean;
|
||||
@@ -845,6 +841,10 @@ export interface FeatureToggles {
|
||||
*/
|
||||
teamHttpHeadersMimir?: boolean;
|
||||
/**
|
||||
* Enables LBAC for datasources for Tempo to apply LBAC filtering of traces to the client requests for users in teams
|
||||
*/
|
||||
teamHttpHeadersTempo?: boolean;
|
||||
/**
|
||||
* Test feature toggle to see how cohorts could be set up AB testing
|
||||
* @default false
|
||||
*/
|
||||
@@ -1028,4 +1028,9 @@ export interface FeatureToggles {
|
||||
* Use proxy-based read-only objects for plugin extensions instead of deep cloning
|
||||
*/
|
||||
extensionsReadOnlyProxy?: boolean;
|
||||
/**
|
||||
* Enables restore deleted dashboards feature
|
||||
* @default false
|
||||
*/
|
||||
restoreDashboards?: boolean;
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ export interface GroupByVariableModel extends VariableWithOptions {
|
||||
datasource: DataSourceRef | null;
|
||||
multi: true;
|
||||
allowCustomValue?: boolean;
|
||||
defaultValue?: VariableOption;
|
||||
}
|
||||
|
||||
export interface VariableOption {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import i18n, { InitOptions, ReactOptions, TFunction } from 'i18next';
|
||||
import i18n, { InitOptions, ReactOptions, TFunction as I18NextTFunction } from 'i18next';
|
||||
import LanguageDetector, { DetectorOptions } from 'i18next-browser-languagedetector';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { initReactI18next, setDefaults, setI18n, Trans as I18NextTrans, getI18n } from 'react-i18next';
|
||||
@@ -6,9 +6,9 @@ import { initReactI18next, setDefaults, setI18n, Trans as I18NextTrans, getI18n
|
||||
import { DEFAULT_LANGUAGE, PSEUDO_LOCALE } from './constants';
|
||||
import { initRegionalFormat } from './dates';
|
||||
import { LANGUAGES } from './languages';
|
||||
import { TransProps, TransType } from './types';
|
||||
import { TFunction, TransProps, TransType } from './types';
|
||||
|
||||
let tFunc: TFunction<string[], undefined> | undefined;
|
||||
let tFunc: I18NextTFunction<string[], undefined> | undefined;
|
||||
let transComponent: TransType;
|
||||
|
||||
export async function initPluginTranslations(id: string) {
|
||||
@@ -140,7 +140,7 @@ export function addResourceBundle(language: string, namespace: string, resource:
|
||||
getI18nInstance().addResourceBundle(language, namespace, resource, undefined, true);
|
||||
}
|
||||
|
||||
export function t(id: string, defaultMessage: string, values?: Record<string, unknown>) {
|
||||
export const t: TFunction = (id: string, defaultMessage: string, values?: Record<string, unknown>) => {
|
||||
if (!tFunc) {
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.warn(
|
||||
@@ -156,7 +156,7 @@ export function t(id: string, defaultMessage: string, values?: Record<string, un
|
||||
}
|
||||
|
||||
return tFunc(id, defaultMessage, values);
|
||||
}
|
||||
};
|
||||
|
||||
export function useTranslate() {
|
||||
return { t };
|
||||
|
||||
@@ -23,5 +23,5 @@ export {
|
||||
DEFAULT_LANGUAGE,
|
||||
} from './constants';
|
||||
export { initPluginTranslations, Trans, useTranslate } from './i18n';
|
||||
export type { TransProps } from './types';
|
||||
export type { TFunction, TransProps } from './types';
|
||||
export { formatDate, formatDuration, formatDateRange } from './dates';
|
||||
|
||||
@@ -9,12 +9,6 @@
|
||||
* preventing the code from being importable by plugins or other npm packages making it truly "internal".
|
||||
*
|
||||
*/
|
||||
import { t } from '../i18n';
|
||||
|
||||
type TFunction = typeof t;
|
||||
|
||||
export type { TFunction };
|
||||
export { t };
|
||||
|
||||
export {
|
||||
addResourceBundle,
|
||||
@@ -23,4 +17,5 @@ export {
|
||||
getLanguage,
|
||||
getResolvedLanguage,
|
||||
initializeI18n,
|
||||
t,
|
||||
} from '../i18n';
|
||||
|
||||
@@ -60,4 +60,9 @@ declare function Trans(props: TransProps): React.ReactElement;
|
||||
*/
|
||||
type TransType = typeof Trans;
|
||||
|
||||
export type { UseTranslateHook, TransProps, TransType };
|
||||
/**
|
||||
* Type for the translation function
|
||||
*/
|
||||
type TFunction = (id: string, defaultMessage: string, values?: Record<string, unknown>) => string;
|
||||
|
||||
export type { UseTranslateHook, TransProps, TransType, TFunction };
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"@grafana/runtime": "12.1.0-pre",
|
||||
"@grafana/schema": "12.1.0-pre",
|
||||
"@grafana/ui": "12.1.0-pre",
|
||||
"react-select": "5.10.0",
|
||||
"react-select": "5.10.1",
|
||||
"react-use": "17.6.0",
|
||||
"rxjs": "7.8.2",
|
||||
"tslib": "2.8.1"
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"@lezer/common": "1.2.3",
|
||||
"@lezer/highlight": "1.2.1",
|
||||
"@lezer/lr": "1.4.2",
|
||||
"@prometheus-io/lezer-promql": "0.301.0",
|
||||
"@prometheus-io/lezer-promql": "0.304.0",
|
||||
"@reduxjs/toolkit": "2.5.1",
|
||||
"@types/debounce-promise": "3.1.9",
|
||||
"@types/lodash": "4.17.15",
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
/**
|
||||
* Ensures a string is compatible with Prometheus' UTF-8 handling rules.
|
||||
*
|
||||
* Prometheus has specific rules for handling UTF-8 strings in metric names and label values:
|
||||
* - Legacy names (matching pattern [a-zA-Z_:][a-zA-Z0-9_:]*) are used as-is
|
||||
* - Non-legacy names containing UTF-8 characters must be wrapped in double quotes
|
||||
*
|
||||
* @param value - The string to make UTF-8 compatible
|
||||
* @returns The original string if it's empty or a valid legacy name, otherwise the string wrapped in double quotes
|
||||
*
|
||||
* @example
|
||||
* utf8Support('metric_name') // returns 'metric_name'
|
||||
* utf8Support('metric-📈') // returns '"metric-📈"'
|
||||
*/
|
||||
export const utf8Support = (value: string) => {
|
||||
if (value === '') {
|
||||
return value;
|
||||
@@ -9,6 +23,22 @@ export const utf8Support = (value: string) => {
|
||||
return `"${value}"`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Escapes a string to make it compatible with Prometheus UTF-8 support.
|
||||
*
|
||||
* This function converts non-legacy name characters to an escaped format:
|
||||
* - Underscores are doubled as '__'
|
||||
* - Valid legacy runes are preserved as-is
|
||||
* - Invalid code points are replaced with '_FFFD_'
|
||||
* - Other characters are converted to '_HEX_' format where HEX is the hexadecimal code point
|
||||
*
|
||||
* @param value - The string to escape
|
||||
* @returns An escaped string prefixed with 'U__' that is compatible with Prometheus
|
||||
*
|
||||
* @example
|
||||
* escapeForUtf8Support("my lovely_http.status:sum") // returns U__my_20_lovely__http_2e_status:sum
|
||||
* escapeForUtf8Support("label with 😱") // returns U__label_20_with_20__1f631_
|
||||
*/
|
||||
export const escapeForUtf8Support = (value: string) => {
|
||||
const isLegacyLabel = isValidLegacyName(value);
|
||||
if (isLegacyLabel) {
|
||||
@@ -42,6 +72,16 @@ export const escapeForUtf8Support = (value: string) => {
|
||||
return escaped;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a string is a valid legacy (the standard) Prometheus metric or label name.
|
||||
*
|
||||
* Valid legacy (the standard) names match the pattern [a-zA-Z_:][a-zA-Z0-9_:]* which means:
|
||||
* - First character must be a letter, underscore, or colon
|
||||
* - Remaining characters can only be letters, numbers, underscores, or colons
|
||||
*
|
||||
* @param name - The string to check
|
||||
* @returns true if the string is a valid legacy (the standard) name, false otherwise
|
||||
*/
|
||||
export const isValidLegacyName = (name: string): boolean => {
|
||||
if (name.length === 0) {
|
||||
return false;
|
||||
@@ -57,9 +97,17 @@ export const isValidLegacyName = (name: string): boolean => {
|
||||
return true;
|
||||
};
|
||||
|
||||
// const labelNamePriorToUtf8Support = /^[a-zA-Z_:][a-zA-Z0-9_:]*$/;
|
||||
// instead of regex we use rune check (converted from prometheus code)
|
||||
// https://github.com/prometheus/common/blob/main/model/metric.go#L426-L428
|
||||
/**
|
||||
* Checks if a character is valid for a legacy (the standard) Prometheus metric or label name.
|
||||
*
|
||||
* This is an implementation of the Prometheus model rune validation logic, which
|
||||
* determines if a character is allowed in a legacy metric or label name.
|
||||
* https://github.com/prometheus/common/blob/v0.64.0/model/metric.go#L430-L432
|
||||
*
|
||||
* @param char - The character to check
|
||||
* @param index - The position of the character in the string
|
||||
* @returns true if the character is valid at the given position, false otherwise
|
||||
*/
|
||||
const isValidLegacyRune = (char: string, index: number): boolean => {
|
||||
const codePoint = char.codePointAt(0);
|
||||
if (codePoint === undefined) {
|
||||
@@ -75,11 +123,26 @@ const isValidLegacyRune = (char: string, index: number): boolean => {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates if a Unicode code point is valid for UTF-8 encoding.
|
||||
*
|
||||
* @param codePoint - The Unicode code point to validate
|
||||
* @returns true if the code point is valid (between 0 and 0x10FFFF), false otherwise
|
||||
*/
|
||||
const isValidCodePoint = (codePoint: number): boolean => {
|
||||
// Validate the code point for UTF-8 compliance if needed.
|
||||
return codePoint >= 0 && codePoint <= 0x10ffff;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps each key in a Prometheus filter string with UTF-8 support.
|
||||
*
|
||||
* This function processes a filter string (e.g. 'metric="value",name=~"pattern"')
|
||||
* and applies UTF-8 support to each key while preserving the operators and values.
|
||||
* It handles quoted values and comma separators correctly.
|
||||
*
|
||||
* @param filterStr - The filter string to process
|
||||
* @returns A new filter string with UTF-8 support applied to the keys
|
||||
*/
|
||||
export const wrapUtf8Filters = (filterStr: string): string => {
|
||||
const resultArray: string[] = [];
|
||||
const operatorRegex = /(=~|!=|!~|=)/; // NOTE: the order of the operators is important here
|
||||
|
||||
@@ -1290,6 +1290,7 @@ export const defaultGroupByVariableKind = (): GroupByVariableKind => ({
|
||||
export interface GroupByVariableSpec {
|
||||
name: string;
|
||||
datasource?: DataSourceRef;
|
||||
defaultValue?: VariableOption;
|
||||
current: VariableOption;
|
||||
options: VariableOption[];
|
||||
multi: boolean;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"lodash": "4.17.21",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-select": "5.10.0",
|
||||
"react-select": "5.10.1",
|
||||
"react-use": "17.6.0",
|
||||
"react-virtualized-auto-sizer": "1.0.25",
|
||||
"rxjs": "7.8.2",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { useState, useMemo, useEffect } from 'react';
|
||||
import { useAsyncFn } from 'react-use';
|
||||
import useDebounce from 'react-use/lib/useDebounce';
|
||||
import { useAsyncFn, useDebounce } from 'react-use';
|
||||
|
||||
import { formattedValueToString, getValueFormat, TimeRange } from '@grafana/data';
|
||||
import { Icon, Spinner, useTheme2 } from '@grafana/ui';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { SelectableValue, TypedVariableModel } from '@grafana/data';
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
"@grafana/schema": "12.1.0-pre",
|
||||
"@hello-pangea/dnd": "17.0.0",
|
||||
"@leeoniya/ufuzzy": "1.0.18",
|
||||
"@monaco-editor/react": "4.6.0",
|
||||
"@monaco-editor/react": "4.7.0",
|
||||
"@popperjs/core": "2.11.8",
|
||||
"@react-aria/dialog": "3.5.25",
|
||||
"@react-aria/focus": "3.20.3",
|
||||
@@ -117,7 +117,7 @@
|
||||
"react-loading-skeleton": "3.5.0",
|
||||
"react-router-dom": "5.3.4",
|
||||
"react-router-dom-v5-compat": "^6.26.1",
|
||||
"react-select": "5.10.0",
|
||||
"react-select": "5.10.1",
|
||||
"react-table": "7.8.0",
|
||||
"react-transition-group": "4.4.5",
|
||||
"react-use": "17.6.0",
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { ActionModel, Field } from '@grafana/data';
|
||||
import { ActionModel, Field, ActionVariableInput } from '@grafana/data';
|
||||
|
||||
import { useTheme2 } from '../../themes';
|
||||
import { t } from '../../utils/i18n';
|
||||
import { Button, ButtonProps } from '../Button';
|
||||
import { ConfirmModal } from '../ConfirmModal/ConfirmModal';
|
||||
|
||||
import { VariablesInputModal } from './VariablesInputModal';
|
||||
|
||||
type ActionButtonProps = ButtonProps & {
|
||||
action: ActionModel<Field>;
|
||||
};
|
||||
@@ -21,27 +23,52 @@ export function ActionButton({ action, ...buttonProps }: ActionButtonProps) {
|
||||
|
||||
const [showConfirm, setShowConfirm] = useState(false);
|
||||
|
||||
// Action variables
|
||||
const [showVarsModal, setShowVarsModal] = useState(false);
|
||||
const [actionVars, setActionVars] = useState<ActionVariableInput>({});
|
||||
|
||||
const actionHasVariables = action.variables && action.variables.length > 0;
|
||||
|
||||
const onClick = () => {
|
||||
if (actionHasVariables) {
|
||||
setShowVarsModal(true);
|
||||
} else {
|
||||
setShowConfirm(true);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
onClick={() => setShowConfirm(true)}
|
||||
onClick={onClick}
|
||||
{...buttonProps}
|
||||
style={{ width: 'fit-content', backgroundColor, color: textColor }}
|
||||
>
|
||||
{action.title}
|
||||
</Button>
|
||||
|
||||
{actionHasVariables && showVarsModal && (
|
||||
<VariablesInputModal
|
||||
onDismiss={() => setShowVarsModal(false)}
|
||||
action={action}
|
||||
onShowConfirm={() => setShowConfirm(true)}
|
||||
variables={actionVars}
|
||||
setVariables={setActionVars}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showConfirm && (
|
||||
<ConfirmModal
|
||||
isOpen={true}
|
||||
title={t('grafana-ui.action-editor.button.confirm-action', 'Confirm action')}
|
||||
body={action.confirmation}
|
||||
body={action.confirmation(actionVars)}
|
||||
confirmText={t('grafana-ui.action-editor.button.confirm', 'Confirm')}
|
||||
confirmButtonVariant="primary"
|
||||
onConfirm={() => {
|
||||
setShowConfirm(false);
|
||||
action.onClick(new MouseEvent('click'));
|
||||
action.onClick(new MouseEvent('click'), null, actionVars);
|
||||
}}
|
||||
onDismiss={() => {
|
||||
setShowConfirm(false);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { ActionModel, ActionVariableInput } from '@grafana/data';
|
||||
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { t } from '../../utils/i18n';
|
||||
import { Button } from '../Button/Button';
|
||||
import { Field } from '../Forms/Field';
|
||||
import { FieldSet } from '../Forms/FieldSet';
|
||||
import { Input } from '../Input/Input';
|
||||
import { Modal } from '../Modal/Modal';
|
||||
|
||||
interface Props {
|
||||
action: ActionModel;
|
||||
onDismiss: () => void;
|
||||
onShowConfirm: () => void;
|
||||
variables: ActionVariableInput;
|
||||
setVariables: (vars: ActionVariableInput) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function VariablesInputModal({ action, onDismiss, onShowConfirm, variables, setVariables }: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const onModalContinue = () => {
|
||||
onDismiss();
|
||||
onShowConfirm();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={true}
|
||||
title={t('grafana-ui.action-editor.button.action-variables-title', 'Action variables')}
|
||||
onDismiss={onDismiss}
|
||||
className={styles.variablesModal}
|
||||
>
|
||||
<FieldSet>
|
||||
{action.variables!.map((variable) => (
|
||||
<Field key={variable.name} label={variable.name}>
|
||||
<Input
|
||||
type="text"
|
||||
value={variables[variable.key] ?? ''}
|
||||
onChange={(e) => {
|
||||
setVariables({ ...variables, [variable.key]: e.currentTarget.value });
|
||||
}}
|
||||
placeholder={t('grafana-ui.action-editor.button.variable-value-placeholder', 'Value')}
|
||||
width={20}
|
||||
/>
|
||||
</Field>
|
||||
))}
|
||||
</FieldSet>
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={onDismiss}>
|
||||
{t('grafana-ui.action-editor.close', 'Close')}
|
||||
</Button>
|
||||
<Button variant="primary" onClick={onModalContinue}>
|
||||
{t('grafana-ui.action-editor.continue', 'Continue')}
|
||||
</Button>
|
||||
</Modal.ButtonRow>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = () => {
|
||||
return {
|
||||
variablesModal: css({
|
||||
zIndex: 10000,
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -100,7 +100,7 @@ export const Examples: StoryFn<typeof Button> = () => {
|
||||
<Button variant="secondary" icon="angle-down" />
|
||||
</ButtonGroup>
|
||||
</Stack>
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>Button inside card</Card.Heading>
|
||||
<Card.Actions>
|
||||
{allButtonVariants.map((variant) => (
|
||||
|
||||
@@ -15,7 +15,7 @@ const meta: Meta<typeof Card> = {
|
||||
tags: ['autodocs', 'nosort'],
|
||||
parameters: {
|
||||
controls: {
|
||||
exclude: ['onClick', 'href', 'heading', 'description', 'className'],
|
||||
exclude: ['onClick', 'href', 'heading', 'description', 'className', 'noMargin'],
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -25,7 +25,7 @@ const meta: Meta<typeof Card> = {
|
||||
*/
|
||||
export const Basic: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<Card {...args}>
|
||||
<Card noMargin {...args}>
|
||||
<Card.Heading>Filter by name</Card.Heading>
|
||||
<Card.Description>
|
||||
Filter data by query. This is useful if you are sharing the results from a different panel that has many queries
|
||||
@@ -41,7 +41,7 @@ export const Basic: StoryFn<typeof Card> = (args) => {
|
||||
*/
|
||||
export const MultipleMetadataElements: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>Test dashboard</Card.Heading>
|
||||
<Card.Meta>{['Folder: Test', 'Views: 100']}</Card.Meta>
|
||||
</Card>
|
||||
@@ -54,7 +54,7 @@ export const MultipleMetadataElements: StoryFn<typeof Card> = (args) => {
|
||||
*/
|
||||
export const ComplexMetadataElements: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>Test dashboard</Card.Heading>
|
||||
<Card.Meta>
|
||||
<>Grafana</>
|
||||
@@ -71,7 +71,7 @@ export const ComplexMetadataElements: StoryFn<typeof Card> = (args) => {
|
||||
*/
|
||||
export const MultipleMetadataWithCustomSeparator: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>Test dashboard</Card.Heading>
|
||||
<Card.Meta separator={'-'}>
|
||||
Grafana
|
||||
@@ -90,7 +90,7 @@ export const MultipleMetadataWithCustomSeparator: StoryFn<typeof Card> = (args)
|
||||
*/
|
||||
export const Tags: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>Test dashboard</Card.Heading>
|
||||
<Card.Description>Card with a list of tags</Card.Description>
|
||||
<Card.Tags>
|
||||
@@ -105,7 +105,7 @@ export const Tags: StoryFn<typeof Card> = (args) => {
|
||||
*/
|
||||
export const AsALink: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<Card href="https://grafana.com">
|
||||
<Card noMargin href="https://grafana.com">
|
||||
<Card.Heading>Redirect to Grafana</Card.Heading>
|
||||
<Card.Description>Clicking this card will redirect to grafana website</Card.Description>
|
||||
</Card>
|
||||
@@ -119,7 +119,7 @@ export const AsALink: StoryFn<typeof Card> = (args) => {
|
||||
*/
|
||||
export const AsAButton: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<Card onClick={() => alert('Hello, Grafana!')}>
|
||||
<Card noMargin onClick={() => alert('Hello, Grafana!')}>
|
||||
<Card.Heading>Hello, Grafana</Card.Heading>
|
||||
<Card.Description>Clicking this card will create an alert</Card.Description>
|
||||
</Card>
|
||||
@@ -131,27 +131,27 @@ export const AsAButton: StoryFn<typeof Card> = (args) => {
|
||||
*/
|
||||
export const InsideAListItem: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<ul style={{ padding: '20px', listStyle: 'none', display: 'grid' }}>
|
||||
<ul style={{ padding: '20px', listStyle: 'none', display: 'grid', gap: '8px' }}>
|
||||
<li>
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>List card item</Card.Heading>
|
||||
<Card.Description>Card that is rendered inside li element.</Card.Description>
|
||||
</Card>
|
||||
</li>
|
||||
<li>
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>List card item</Card.Heading>
|
||||
<Card.Description>Card that is rendered inside li element.</Card.Description>
|
||||
</Card>
|
||||
</li>
|
||||
<li>
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>List card item</Card.Heading>
|
||||
<Card.Description>Card that is rendered inside li element.</Card.Description>
|
||||
</Card>
|
||||
</li>
|
||||
<li>
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>List card item</Card.Heading>
|
||||
<Card.Description>Card that is rendered inside li element.</Card.Description>
|
||||
</Card>
|
||||
@@ -161,11 +161,11 @@ export const InsideAListItem: StoryFn<typeof Card> = (args) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Cards can also be rendered with media content such icons or images. Such elements need to be wrapped in `Card.Figure` component.
|
||||
* Cards can also be rendered with media content such as icons or images. Such elements need to be wrapped in `Card.Figure` component.
|
||||
*/
|
||||
export const WithMediaElements: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>1-ops-tools1-fallback</Card.Heading>
|
||||
<Card.Figure>
|
||||
<img src={logo} alt="Grafana Logo" width="40" height="40" />
|
||||
@@ -187,7 +187,7 @@ export const WithMediaElements: StoryFn<typeof Card> = (args) => {
|
||||
*/
|
||||
export const ActionCards: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<Card {...args}>
|
||||
<Card noMargin {...args}>
|
||||
<Card.Heading>1-ops-tools1-fallback</Card.Heading>
|
||||
<Card.Meta>
|
||||
Prometheus
|
||||
@@ -220,7 +220,7 @@ export const ActionCards: StoryFn<typeof Card> = (args) => {
|
||||
*/
|
||||
export const DisabledState: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<Card disabled>
|
||||
<Card noMargin disabled>
|
||||
<Card.Heading>1-ops-tools1-fallback</Card.Heading>
|
||||
<Card.Meta>
|
||||
Grafana
|
||||
@@ -249,7 +249,7 @@ export const DisabledState: StoryFn<typeof Card> = (args) => {
|
||||
|
||||
export const Selectable: StoryFn<typeof Card> = () => {
|
||||
return (
|
||||
<Card isSelected disabled>
|
||||
<Card noMargin isSelected disabled>
|
||||
<Card.Heading>Option #1</Card.Heading>
|
||||
<Card.Description>This is a really great option, you will not regret it.</Card.Description>
|
||||
<Card.Figure>
|
||||
@@ -261,7 +261,7 @@ export const Selectable: StoryFn<typeof Card> = () => {
|
||||
|
||||
export const Full: StoryFn<typeof Card> = (args) => {
|
||||
return (
|
||||
<Card {...args}>
|
||||
<Card noMargin {...args}>
|
||||
<Card.Heading>Card title</Card.Heading>
|
||||
<Card.Description>
|
||||
Description, body text. Greetings! Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('Card', () => {
|
||||
const user = userEvent.setup();
|
||||
const callback = jest.fn();
|
||||
render(
|
||||
<Card onClick={callback}>
|
||||
<Card noMargin onClick={callback}>
|
||||
<Card.Heading>Test Heading</Card.Heading>
|
||||
</Card>
|
||||
);
|
||||
@@ -22,7 +22,7 @@ describe('Card', () => {
|
||||
describe('Card Actions', () => {
|
||||
it('Children should be disabled or enabled according to Card disabled prop', () => {
|
||||
const { rerender } = render(
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>Test Heading</Card.Heading>
|
||||
<Card.Actions>
|
||||
<Button>Click Me</Button>
|
||||
@@ -37,7 +37,7 @@ describe('Card', () => {
|
||||
expect(screen.getByRole('button', { name: 'Delete' })).toBeEnabled();
|
||||
|
||||
rerender(
|
||||
<Card disabled>
|
||||
<Card noMargin disabled>
|
||||
<Card.Heading>Test Heading</Card.Heading>
|
||||
<Card.Actions>
|
||||
<Button>Click Me</Button>
|
||||
@@ -54,7 +54,7 @@ describe('Card', () => {
|
||||
|
||||
it('Children should be independently enabled or disabled if explicitly set', () => {
|
||||
const { rerender } = render(
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>Test Heading</Card.Heading>
|
||||
<Card.Actions>
|
||||
<Button disabled>Click Me</Button>
|
||||
@@ -69,7 +69,7 @@ describe('Card', () => {
|
||||
expect(screen.getByRole('button', { name: 'Delete' })).toBeDisabled();
|
||||
|
||||
rerender(
|
||||
<Card disabled>
|
||||
<Card noMargin disabled>
|
||||
<Card.Heading>Test Heading</Card.Heading>
|
||||
<Card.Actions>
|
||||
<Button disabled={false}>Click Me</Button>
|
||||
@@ -87,7 +87,7 @@ describe('Card', () => {
|
||||
it('Children should be conditional', () => {
|
||||
const shouldNotRender = false;
|
||||
render(
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>Test Heading</Card.Heading>
|
||||
<Card.Actions>
|
||||
<Button>Click Me</Button>
|
||||
@@ -105,7 +105,7 @@ describe('Card', () => {
|
||||
|
||||
it('Should allow selectable cards', () => {
|
||||
const { rerender } = render(
|
||||
<Card isSelected={true}>
|
||||
<Card noMargin isSelected={true}>
|
||||
<Card.Heading>My Option</Card.Heading>
|
||||
</Card>
|
||||
);
|
||||
@@ -114,7 +114,7 @@ describe('Card', () => {
|
||||
expect(screen.getByRole('radio')).toBeChecked();
|
||||
|
||||
rerender(
|
||||
<Card isSelected={false}>
|
||||
<Card noMargin isSelected={false}>
|
||||
<Card.Heading>My Option</Card.Heading>
|
||||
</Card>
|
||||
);
|
||||
@@ -123,7 +123,7 @@ describe('Card', () => {
|
||||
expect(screen.getByRole('radio')).not.toBeChecked();
|
||||
|
||||
rerender(
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>My Option</Card.Heading>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Input, Props as InputProps } from '../Input/Input';
|
||||
import { Portal } from '../Portal/Portal';
|
||||
|
||||
import { ComboboxList } from './ComboboxList';
|
||||
import { SuffixIcon } from './SuffixIcon';
|
||||
import { itemToString } from './filter';
|
||||
import { getComboboxStyles, MENU_OPTION_HEIGHT, MENU_OPTION_HEIGHT_DESCRIPTION } from './getComboboxStyles';
|
||||
import { ComboboxOption } from './types';
|
||||
@@ -129,7 +130,6 @@ export const Combobox = <T extends string | number>(props: ComboboxProps<T>) =>
|
||||
autoFocus,
|
||||
onBlur,
|
||||
disabled,
|
||||
loading,
|
||||
invalid,
|
||||
} = props;
|
||||
|
||||
@@ -330,12 +330,7 @@ export const Combobox = <T extends string | number>(props: ComboboxProps<T>) =>
|
||||
const InputComponent = isAutoSize ? AutoSizeInput : Input;
|
||||
const placeholder = (isOpen ? itemToString(selectedItem) : null) || placeholderProp;
|
||||
|
||||
const suffixIcon = asyncLoading
|
||||
? 'spinner'
|
||||
: // If it's loading, show loading icon. Otherwise, icon indicating menu state
|
||||
isOpen
|
||||
? 'search'
|
||||
: 'angle-down';
|
||||
const loading = props.loading || asyncLoading;
|
||||
|
||||
const inputSuffix = (
|
||||
<>
|
||||
@@ -357,7 +352,7 @@ export const Combobox = <T extends string | number>(props: ComboboxProps<T>) =>
|
||||
/>
|
||||
)}
|
||||
|
||||
<Icon name={suffixIcon} />
|
||||
<SuffixIcon isLoading={loading || false} isOpen={isOpen} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -369,7 +364,6 @@ export const Combobox = <T extends string | number>(props: ComboboxProps<T>) =>
|
||||
autoFocus={autoFocus}
|
||||
onBlur={onBlur}
|
||||
disabled={disabled}
|
||||
loading={loading}
|
||||
invalid={invalid}
|
||||
className={styles.input}
|
||||
suffix={inputSuffix}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { autoUpdate, flip, offset, shift, useFloating } from '@floating-ui/react
|
||||
import Prism, { Grammar, LanguageMap } from 'prismjs';
|
||||
import { memo, useEffect, useRef, useState } from 'react';
|
||||
import * as React from 'react';
|
||||
import usePrevious from 'react-use/lib/usePrevious';
|
||||
import { usePrevious } from 'react-use';
|
||||
import { Value } from 'slate';
|
||||
import Plain from 'slate-plain-serializer';
|
||||
import { Editor } from 'slate-react';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { css, cx } from '@emotion/css';
|
||||
import { groupBy, capitalize } from 'lodash';
|
||||
import { useRef, useMemo } from 'react';
|
||||
import * as React from 'react';
|
||||
import useClickAway from 'react-use/lib/useClickAway';
|
||||
import { useClickAway } from 'react-use';
|
||||
|
||||
import { VariableSuggestion, GrafanaTheme2 } from '@grafana/data';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { forwardRef, HTMLProps, ReactNode, useContext } from 'react';
|
||||
import useMeasure from 'react-use/lib/useMeasure';
|
||||
import { useMeasure } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { action } from '@storybook/addon-actions';
|
||||
import { Meta, StoryFn } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
import * as React from 'react';
|
||||
import { AsyncState } from 'react-use/lib/useAsync';
|
||||
import { type AsyncState } from 'react-use/lib/useAsync';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { isObject } from 'lodash';
|
||||
import { HTMLProps } from 'react';
|
||||
import * as React from 'react';
|
||||
import { useAsyncFn } from 'react-use';
|
||||
import { AsyncState } from 'react-use/lib/useAsync';
|
||||
import { type AsyncState } from 'react-use/lib/useAsync';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { cx, css } from '@emotion/css';
|
||||
import { HTMLProps, useRef, useState } from 'react';
|
||||
import * as React from 'react';
|
||||
import useClickAway from 'react-use/lib/useClickAway';
|
||||
import { useClickAway } from 'react-use';
|
||||
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { measureText } from '../../utils/measureText';
|
||||
|
||||
@@ -19,6 +19,7 @@ describe('DataLinksActionsTooltip', () => {
|
||||
const mockAction: ActionModel = {
|
||||
title: 'Action1',
|
||||
onClick: jest.fn(),
|
||||
confirmation: jest.fn(),
|
||||
style: { backgroundColor: '#ff0000' },
|
||||
};
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ export const ThemeDemo = () => {
|
||||
Disabled
|
||||
</Button>
|
||||
</Stack>
|
||||
<Card>
|
||||
<Card noMargin>
|
||||
<Card.Heading>Button inside card</Card.Heading>
|
||||
<Card.Actions>
|
||||
{allButtonVariants.map((variant) => (
|
||||
@@ -259,8 +259,8 @@ export function VizHuesDemo({ theme, color }: VizHuesDemoProps) {
|
||||
return (
|
||||
<tr>
|
||||
<td>{color.name}</td>
|
||||
{color.shades.map((shade) => (
|
||||
<td>
|
||||
{color.shades.map((shade, index) => (
|
||||
<td key={index}>
|
||||
<div
|
||||
className={css({
|
||||
background: shade.color,
|
||||
|
||||
@@ -228,8 +228,11 @@ export const TooltipPlugin2 = ({
|
||||
|
||||
// in some ways this is similar to ClickOutsideWrapper.tsx
|
||||
const downEventOutside = (e: Event) => {
|
||||
// this tooltip is Portaled, but actions inside it create forms in Modals
|
||||
const isModalOrPortaled = '[role="dialog"], #grafana-portal-container';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
if (!domRef.current!.contains(e.target as Node)) {
|
||||
if ((e.target as HTMLElement).closest(isModalOrPortaled) == null) {
|
||||
dismiss();
|
||||
}
|
||||
};
|
||||
|
||||
+15
-12
@@ -63,18 +63,21 @@ done
|
||||
export HOME="$GF_PATHS_HOME"
|
||||
|
||||
if [ ! -z "${GF_INSTALL_PLUGINS}" ]; then
|
||||
OLDIFS=$IFS
|
||||
IFS=','
|
||||
for plugin in ${GF_INSTALL_PLUGINS}; do
|
||||
IFS=$OLDIFS
|
||||
if [[ $plugin =~ .*\;.* ]]; then
|
||||
pluginUrl=$(echo "$plugin" | cut -d';' -f 1)
|
||||
pluginInstallFolder=$(echo "$plugin" | cut -d';' -f 2)
|
||||
grafana cli --pluginUrl ${pluginUrl} --pluginsDir "${GF_PATHS_PLUGINS}" plugins install "${pluginInstallFolder}"
|
||||
else
|
||||
grafana cli --pluginsDir "${GF_PATHS_PLUGINS}" plugins install ${plugin}
|
||||
fi
|
||||
done
|
||||
>&2 echo "\033[0;33mWARN\033[0m: GF_INSTALL_PLUGINS is deprecated. Use GF_PLUGINS_PREINSTALL or GF_PLUGINS_PREINSTALL_SYNC instead. Checkout the documentation for more info."
|
||||
if [ "${GF_INSTALL_PLUGINS_FORCE}" = "true" ]; then
|
||||
OLDIFS=$IFS
|
||||
IFS=','
|
||||
for plugin in ${GF_INSTALL_PLUGINS}; do
|
||||
IFS=$OLDIFS
|
||||
if [[ $plugin =~ .*\;.* ]]; then
|
||||
pluginUrl=$(echo "$plugin" | cut -d';' -f 1)
|
||||
pluginInstallFolder=$(echo "$plugin" | cut -d';' -f 2)
|
||||
grafana cli --pluginUrl ${pluginUrl} --pluginsDir "${GF_PATHS_PLUGINS}" plugins install "${pluginInstallFolder}"
|
||||
else
|
||||
grafana cli --pluginsDir "${GF_PATHS_PLUGINS}" plugins install ${plugin}
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
exec grafana server \
|
||||
|
||||
@@ -12,9 +12,12 @@ runs:
|
||||
go-version-file: go.mod
|
||||
- name: Bump versions
|
||||
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
|
||||
env:
|
||||
GO_MOD_DIR: ${{ inputs.go-mod-dir }}
|
||||
VERSION: ${{ inputs.version }}
|
||||
with:
|
||||
verb: run
|
||||
args: go run ./pkg/build/actions/bump-version -version=${{ inputs.version }}
|
||||
args: go run ./pkg/build/actions/bump-version -version=${VERSION}
|
||||
- name: make gen-cue
|
||||
shell: bash
|
||||
run: make gen-cue
|
||||
|
||||
@@ -24,7 +24,6 @@ dagger run --silent go run ./pkg/build/cmd \
|
||||
-a docker:grafana:linux/arm/v7 \
|
||||
--yarn-cache=${YARN_CACHE_FOLDER} \
|
||||
--checksum \
|
||||
--verify \
|
||||
--build-id=${DRONE_BUILD_NUMBER} \
|
||||
--grafana-dir=${GRAFANA_DIR} \
|
||||
--github-token=${GITHUB_TOKEN} \
|
||||
@@ -36,4 +35,4 @@ echo "Final list of artifacts:"
|
||||
cat assets.txt
|
||||
|
||||
# Move the tar.gz packages to their expected locations
|
||||
cat assets.txt | IS_MAIN=true go run ./scripts/move_packages.go ./dist/main
|
||||
cat assets.txt | IS_MAIN=true go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/main
|
||||
|
||||
@@ -19,13 +19,11 @@ dagger run --silent go run ./pkg/build/cmd \
|
||||
-a docker:enterprise:linux/arm64 \
|
||||
--yarn-cache=${YARN_CACHE_FOLDER} \
|
||||
--checksum \
|
||||
--verify \
|
||||
--build-id=${DRONE_BUILD_NUMBER} \
|
||||
--grafana-ref=${SOURCE_COMMIT} \
|
||||
--grafana-repo="https://github.com/grafana/grafana.git" \
|
||||
--enterprise-ref=${DRONE_COMMIT} \
|
||||
--github-token=${GITHUB_TOKEN} \
|
||||
--go-version=${GO_VERSION} \
|
||||
--ubuntu-base=${UBUNTU_BASE} \
|
||||
--alpine-base=${ALPINE_BASE} \
|
||||
--patches-repo=${PATCHES_REPO} \
|
||||
@@ -35,4 +33,4 @@ dagger run --silent go run ./pkg/build/cmd \
|
||||
cat assets.txt
|
||||
|
||||
# Move the tar.gz packages to their expected locations
|
||||
cat assets.txt | DESTINATION=gs://grafana-downloads IS_MAIN=true go run ./scripts/move_packages.go ./dist/main
|
||||
cat assets.txt | DESTINATION=gs://grafana-downloads IS_MAIN=true go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/main
|
||||
|
||||
@@ -15,13 +15,11 @@ dagger run --silent go run ./pkg/build/cmd \
|
||||
-a frontend:enterprise \
|
||||
--yarn-cache=${YARN_CACHE_FOLDER} \
|
||||
--checksum \
|
||||
--verify \
|
||||
--build-id=${DRONE_BUILD_NUMBER} \
|
||||
--grafana-ref=${SOURCE_COMMIT} \
|
||||
--grafana-repo="https://github.com/grafana/grafana.git" \
|
||||
--enterprise-ref=${DRONE_COMMIT} \
|
||||
--github-token=${GITHUB_TOKEN} \
|
||||
--go-version=${GO_VERSION} \
|
||||
--ubuntu-base=${UBUNTU_BASE} \
|
||||
--alpine-base=${ALPINE_BASE} \
|
||||
--patches-repo=${PATCHES_REPO} \
|
||||
@@ -30,5 +28,5 @@ dagger run --silent go run ./pkg/build/cmd \
|
||||
|
||||
echo "Final list of artifacts:"
|
||||
# Move the tar.gz packages to their expected locations
|
||||
cat assets.txt | grep -v "public" | DESTINATION=gs://grafana-downloads-enterprise2 IS_MAIN=true go run ./scripts/move_packages.go ./dist/main
|
||||
cat assets.txt | grep "public" | DESTINATION=gs://grafana-static-assets IS_MAIN=true go run ./scripts/move_packages.go ./dist/cdn
|
||||
cat assets.txt | grep -v "public" | DESTINATION=gs://grafana-downloads-enterprise2 IS_MAIN=true go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/main
|
||||
cat assets.txt | grep "public" | DESTINATION=gs://grafana-static-assets IS_MAIN=true go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/cdn
|
||||
|
||||
@@ -12,7 +12,7 @@ docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all
|
||||
# -a deb:enterprise:linux/arm/v7:nightly \
|
||||
# -a docker:enterprise:linux/arm/v7 \
|
||||
# -a docker:enterprise:linux/arm/v7:ubuntu \
|
||||
|
||||
|
||||
dagger run --silent go run ./pkg/build/cmd \
|
||||
artifacts \
|
||||
-a targz:enterprise:linux/amd64 \
|
||||
@@ -46,7 +46,6 @@ dagger run --silent go run ./pkg/build/cmd \
|
||||
--github-token=${GITHUB_TOKEN} \
|
||||
--destination=${local_dst} \
|
||||
--yarn-cache=${YARN_CACHE_FOLDER} \
|
||||
--go-version=${GO_VERSION} \
|
||||
--ubuntu-base="${UBUNTU_BASE}" \
|
||||
--alpine-base="${ALPINE_BASE}" > assets.txt
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ dagger run --silent go run ./pkg/build/cmd \
|
||||
--github-token=${GITHUB_TOKEN} \
|
||||
--destination=${local_dst} \
|
||||
--yarn-cache=${YARN_CACHE_FOLDER} \
|
||||
--go-version=${GO_VERSION} \
|
||||
--ubuntu-base="${UBUNTU_BASE}" \
|
||||
--alpine-base="${ALPINE_BASE}" > assets.txt
|
||||
|
||||
|
||||
@@ -42,11 +42,10 @@ dagger run --silent go run ./pkg/build/cmd \
|
||||
--grafana-ref=${DRONE_TAG} \
|
||||
--grafana-repo=https://github.com/grafana/grafana-security-mirror.git \
|
||||
--github-token=${GITHUB_TOKEN} \
|
||||
--go-version=${GO_VERSION} \
|
||||
--ubuntu-base="${UBUNTU_BASE}" \
|
||||
--alpine-base="${ALPINE_BASE}" \
|
||||
--version=${DRONE_TAG} \
|
||||
--destination=${local_dst} > assets.txt
|
||||
|
||||
# Move the tar.gz packages to their expected locations
|
||||
cat assets.txt | go run ./scripts/move_packages.go ./dist/prerelease
|
||||
cat assets.txt | go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/prerelease
|
||||
|
||||
@@ -39,10 +39,9 @@ dagger run --silent go run ./pkg/build/cmd \
|
||||
--build-id=${DRONE_BUILD_NUMBER} \
|
||||
--grafana-dir=${GRAFANA_DIR} \
|
||||
--github-token=${GITHUB_TOKEN} \
|
||||
--go-version=${GO_VERSION} \
|
||||
--ubuntu-base="${UBUNTU_BASE}" \
|
||||
--alpine-base="${ALPINE_BASE}" \
|
||||
--version=${DRONE_TAG} \
|
||||
--destination=${local_dst} > assets.txt
|
||||
|
||||
cat assets.txt | go run ./scripts/move_packages.go ./dist/prerelease
|
||||
cat assets.txt | go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/prerelease
|
||||
|
||||
@@ -33,10 +33,9 @@ dagger run --silent go run ./pkg/build/cmd \
|
||||
--grafana-repo=https://github.com/grafana/grafana-security-mirror.git \
|
||||
--github-token=${GITHUB_TOKEN} \
|
||||
--version=${DRONE_TAG} \
|
||||
--go-version=${GO_VERSION} \
|
||||
--ubuntu-base="${UBUNTU_BASE}" \
|
||||
--alpine-base="${ALPINE_BASE}" \
|
||||
--destination=${local_dst} > assets.txt
|
||||
|
||||
# Move the tar.gz packages to their expected locations
|
||||
cat assets.txt | go run ./scripts/move_packages.go ./dist/prerelease
|
||||
cat assets.txt | go run ./pkg/build/daggerbuild/scripts/move_packages.go ./dist/prerelease
|
||||
|
||||
@@ -6,7 +6,6 @@ package extensions
|
||||
import (
|
||||
_ "cloud.google.com/go/kms/apiv1"
|
||||
_ "cloud.google.com/go/kms/apiv1/kmspb"
|
||||
_ "cloud.google.com/go/spanner"
|
||||
_ "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
_ "github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys"
|
||||
_ "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault"
|
||||
@@ -30,6 +29,11 @@ import (
|
||||
_ "github.com/spf13/cobra" // used by the standalone apiserver cli
|
||||
_ "github.com/stretchr/testify/require"
|
||||
_ "golang.org/x/time/rate"
|
||||
_ "k8s.io/api"
|
||||
_ "k8s.io/kube-aggregator/pkg/apiserver/scheme"
|
||||
_ "k8s.io/kube-aggregator/pkg/generated/openapi"
|
||||
_ "k8s.io/kube-aggregator/pkg/registry/apiservice/rest"
|
||||
_ "sigs.k8s.io/randfill"
|
||||
_ "xorm.io/builder"
|
||||
|
||||
_ "github.com/grafana/authlib/authn"
|
||||
@@ -42,8 +46,4 @@ import (
|
||||
_ "github.com/grafana/e2e"
|
||||
_ "github.com/grafana/gofpdf"
|
||||
_ "github.com/grafana/gomemcache/memcache"
|
||||
|
||||
_ "k8s.io/kube-aggregator/pkg/apiserver/scheme"
|
||||
_ "k8s.io/kube-aggregator/pkg/generated/openapi"
|
||||
_ "k8s.io/kube-aggregator/pkg/registry/apiservice/rest"
|
||||
)
|
||||
|
||||
@@ -54,7 +54,6 @@ type InitTestDBOpt = sqlstore.InitTestDBOpt
|
||||
var SetupTestDB = sqlstore.SetupTestDB
|
||||
var CleanupTestDB = sqlstore.CleanupTestDB
|
||||
var ProvideService = sqlstore.ProvideService
|
||||
var SkipTestsOnSpanner = sqlstore.SkipTestsOnSpanner
|
||||
|
||||
func InitTestDB(t sqlutil.ITestDB, opts ...InitTestDBOpt) *sqlstore.SQLStore {
|
||||
db, _ := InitTestDBWithCfg(t, opts...)
|
||||
@@ -96,11 +95,3 @@ func IsTestDBMSSQL() bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func IsTestDBSpanner() bool {
|
||||
if db, present := os.LookupEnv("GRAFANA_TEST_DB"); present {
|
||||
return db == migrator.Spanner
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -374,10 +374,6 @@ func (sl *ServerLockService) createLock(ctx context.Context,
|
||||
}
|
||||
lockRow.Id = id
|
||||
} else {
|
||||
if sl.SQLStore.GetDBType() == migrator.Spanner {
|
||||
rawSQL += " THEN RETURN id" // Required for successful LastInsertId call.
|
||||
}
|
||||
|
||||
res, err := dbSession.Exec(
|
||||
rawSQL,
|
||||
lockRow.OperationUID, lockRow.LastExecution, 0)
|
||||
|
||||
@@ -186,11 +186,6 @@ func (r *rowsWrapper) ContinueToken() string {
|
||||
return r.row.token.String()
|
||||
}
|
||||
|
||||
// ContinueTokenWithCurrentRV implements resource.ListIterator.
|
||||
func (r *rowsWrapper) ContinueTokenWithCurrentRV() string {
|
||||
return r.row.token.String()
|
||||
}
|
||||
|
||||
// Error implements resource.ListIterator.
|
||||
func (r *rowsWrapper) Error() error {
|
||||
return r.err
|
||||
|
||||
@@ -229,7 +229,12 @@ func (a *dashboardSqlAccess) ReadResource(ctx context.Context, req *resourcepb.R
|
||||
return rsp
|
||||
}
|
||||
|
||||
// List implements AppendingStore.
|
||||
// ListHistory implements StorageBackend.
|
||||
func (a *dashboardSqlAccess) ListHistory(ctx context.Context, req *resourcepb.ListRequest, cb func(resource.ListIterator) error) (int64, error) {
|
||||
return a.ListIterator(ctx, req, cb)
|
||||
}
|
||||
|
||||
// List implements StorageBackend.
|
||||
func (a *dashboardSqlAccess) ListIterator(ctx context.Context, req *resourcepb.ListRequest, cb func(resource.ListIterator) error) (int64, error) {
|
||||
if req.ResourceVersion != 0 {
|
||||
return 0, apierrors.NewBadRequest("List with explicit resourceVersion is not supported with this storage backend")
|
||||
|
||||
@@ -304,9 +304,7 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resourcepb.Reso
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hits := formatQueryResult(res)
|
||||
|
||||
for _, dashboard := range hits {
|
||||
for _, dashboard := range res {
|
||||
tags, err := json.Marshal(dashboard.Tags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -324,7 +322,7 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resourcepb.Reso
|
||||
}
|
||||
|
||||
list.Results.Rows = append(list.Results.Rows, &resourcepb.ResourceTableRow{
|
||||
Key: getResourceKey(dashboard, req.Options.Key.Namespace),
|
||||
Key: getResourceKey(&dashboard, req.Options.Key.Namespace),
|
||||
Cells: cells,
|
||||
})
|
||||
}
|
||||
@@ -352,35 +350,6 @@ func getResourceKey(item *dashboards.DashboardSearchProjection, namespace string
|
||||
}
|
||||
}
|
||||
|
||||
func formatQueryResult(res []dashboards.DashboardSearchProjection) []*dashboards.DashboardSearchProjection {
|
||||
hitList := make([]*dashboards.DashboardSearchProjection, 0)
|
||||
hits := make(map[string]*dashboards.DashboardSearchProjection)
|
||||
|
||||
for _, item := range res {
|
||||
key := fmt.Sprintf("%s-%d", item.UID, item.OrgID)
|
||||
hit, exists := hits[key]
|
||||
if !exists {
|
||||
hit = &dashboards.DashboardSearchProjection{
|
||||
ID: item.ID,
|
||||
UID: item.UID,
|
||||
Title: item.Title,
|
||||
FolderUID: item.FolderUID,
|
||||
Tags: []string{},
|
||||
IsFolder: item.IsFolder,
|
||||
SortMeta: item.SortMeta,
|
||||
}
|
||||
hitList = append(hitList, hit)
|
||||
hits[key] = hit
|
||||
}
|
||||
|
||||
if len(item.Term) > 0 {
|
||||
hit.Tags = append(hit.Tags, item.Term)
|
||||
}
|
||||
}
|
||||
|
||||
return hitList
|
||||
}
|
||||
|
||||
func (c *DashboardSearchClient) GetStats(ctx context.Context, req *resourcepb.ResourceStatsRequest, _ ...grpc.CallOption) (*resourcepb.ResourceStatsResponse, error) {
|
||||
info, err := claims.ParseNamespace(req.Namespace)
|
||||
if err != nil {
|
||||
|
||||
@@ -44,8 +44,8 @@ func TestDashboardSearchClient_Search(t *testing.T) {
|
||||
Type: "dash-db", // should set type based off of key
|
||||
Sort: sorter,
|
||||
}).Return([]dashboards.DashboardSearchProjection{
|
||||
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1", Term: "term"},
|
||||
{ID: 2, UID: "uid2", Title: "Test Dashboard2", FolderUID: "folder2"},
|
||||
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1", Tags: []string{"term"}},
|
||||
{ID: 2, UID: "uid2", Title: "Test Dashboard2", FolderUID: "folder2", Tags: []string{}},
|
||||
}, nil).Once()
|
||||
|
||||
req := &resourcepb.ResourceSearchRequest{
|
||||
@@ -121,7 +121,7 @@ func TestDashboardSearchClient_Search(t *testing.T) {
|
||||
Type: "dash-db",
|
||||
Sort: sortOptionAsc,
|
||||
}).Return([]dashboards.DashboardSearchProjection{
|
||||
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(50)},
|
||||
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(50), Tags: []string{}},
|
||||
}, nil).Once()
|
||||
|
||||
req := &resourcepb.ResourceSearchRequest{
|
||||
@@ -186,7 +186,7 @@ func TestDashboardSearchClient_Search(t *testing.T) {
|
||||
Type: "dash-db",
|
||||
Sort: sortOptionAsc,
|
||||
}).Return([]dashboards.DashboardSearchProjection{
|
||||
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(2)},
|
||||
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(2), Tags: []string{}},
|
||||
}, nil).Once()
|
||||
|
||||
req := &resourcepb.ResourceSearchRequest{
|
||||
|
||||
@@ -254,7 +254,7 @@ func (b *QueryAPIBuilder) handleQuerySingleDatasource(ctx context.Context, req d
|
||||
return &backend.QueryDataResponse{}, nil
|
||||
}
|
||||
|
||||
client, err := b.client.GetDataSourceClient(
|
||||
client, err := b.clientSupplier.GetDataSourceClient(
|
||||
ctx,
|
||||
v0alpha1.DataSourceRef{
|
||||
Type: req.PluginId,
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
|
||||
func TestQueryRestConnectHandler(t *testing.T) {
|
||||
b := &QueryAPIBuilder{
|
||||
client: mockClient{
|
||||
clientSupplier: mockClient{
|
||||
lastCalledWithHeaders: &map[string]string{},
|
||||
},
|
||||
tracer: tracing.InitializeTracerForTest(),
|
||||
@@ -80,7 +80,7 @@ func TestQueryRestConnectHandler(t *testing.T) {
|
||||
"X-Rule-Type": "type-1",
|
||||
"X-Rule-Version": "version-1",
|
||||
"X-Grafana-Org-Id": "1",
|
||||
}, *b.client.(mockClient).lastCalledWithHeaders)
|
||||
}, *b.clientSupplier.(mockClient).lastCalledWithHeaders)
|
||||
}
|
||||
|
||||
func TestInstantQueryFromAlerting(t *testing.T) {
|
||||
|
||||
@@ -43,17 +43,17 @@ type QueryAPIBuilder struct {
|
||||
|
||||
authorizer authorizer.Authorizer
|
||||
|
||||
tracer tracing.Tracer
|
||||
metrics *metrics.ExprMetrics
|
||||
parser *queryParser
|
||||
client clientapi.DataSourceClientSupplier
|
||||
registry query.DataSourceApiServerRegistry
|
||||
converter *expr.ResultConverter
|
||||
queryTypes *query.QueryTypeDefinitionList
|
||||
tracer tracing.Tracer
|
||||
metrics *metrics.ExprMetrics
|
||||
parser *queryParser
|
||||
clientSupplier clientapi.DataSourceClientSupplier
|
||||
registry query.DataSourceApiServerRegistry
|
||||
converter *expr.ResultConverter
|
||||
queryTypes *query.QueryTypeDefinitionList
|
||||
}
|
||||
|
||||
func NewQueryAPIBuilder(features featuremgmt.FeatureToggles,
|
||||
client clientapi.DataSourceClientSupplier,
|
||||
clientSupplier clientapi.DataSourceClientSupplier,
|
||||
ar authorizer.Authorizer,
|
||||
registry query.DataSourceApiServerRegistry,
|
||||
legacy service.LegacyDataSourceLookup,
|
||||
@@ -80,7 +80,7 @@ func NewQueryAPIBuilder(features featuremgmt.FeatureToggles,
|
||||
return &QueryAPIBuilder{
|
||||
concurrentQueryLimit: 4,
|
||||
log: log.New("query_apiserver"),
|
||||
client: client,
|
||||
clientSupplier: clientSupplier,
|
||||
authorizer: ar,
|
||||
registry: registry,
|
||||
parser: newQueryParser(reader, legacy, tracer, log.New("query_parser")),
|
||||
|
||||
@@ -203,10 +203,6 @@ func TestIntegrationAnnotationListingWithInheritedRBAC(t *testing.T) {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
if db.IsTestDBSpanner() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
orgID := int64(1)
|
||||
permissions := []accesscontrol.Permission{
|
||||
{
|
||||
|
||||
@@ -334,7 +334,6 @@ func (r *xormRepositoryImpl) Get(ctx context.Context, query annotations.ItemQuer
|
||||
}
|
||||
|
||||
if len(tags) > 0 {
|
||||
// "at" is a keyword in Spanner and needs to be quoted.
|
||||
tagsSubQuery := fmt.Sprintf(`
|
||||
SELECT SUM(1) FROM annotation_tag `+r.db.Quote("at")+`
|
||||
INNER JOIN tag on tag.id = `+r.db.Quote("at")+`.tag_id
|
||||
|
||||
@@ -146,14 +146,11 @@ func (s *AnonDBStore) CreateOrUpdateDevice(ctx context.Context, device *Device)
|
||||
}
|
||||
|
||||
// If CreatedAt time is not set (i.e. it's zero), and we end up creating the device, use current time as creation time.
|
||||
// Spanner converts zero time to NULL, but CreatedAt is not nullable, so this helps to fix that problem too.
|
||||
// If database converts zero time to NULL, but CreatedAt is not nullable, this helps to fix that problem too.
|
||||
created := device.CreatedAt
|
||||
if created.IsZero() {
|
||||
created = time.Now()
|
||||
}
|
||||
if s.sqlStore.GetDBType() == migrator.Spanner {
|
||||
return s.insertIntoSpanner(ctx, device, created)
|
||||
}
|
||||
|
||||
args := []any{device.DeviceID, device.ClientIP, device.UserAgent, created.UTC(), device.UpdatedAt.UTC()}
|
||||
switch s.sqlStore.GetDBType() {
|
||||
@@ -192,30 +189,6 @@ func (s *AnonDBStore) CreateOrUpdateDevice(ctx context.Context, device *Device)
|
||||
return err
|
||||
}
|
||||
|
||||
// In Spanner INSERT OR UPDATE only works when conflict is on primary key. However here we expect conflict on non-PK
|
||||
// column "device_id", so we need to use a transaction instead.
|
||||
func (s *AnonDBStore) insertIntoSpanner(ctx context.Context, dev *Device, created time.Time) error {
|
||||
return s.sqlStore.WithTransactionalDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
||||
prev := &Device{DeviceID: dev.DeviceID}
|
||||
ok, err := dbSession.Table(tableName).Get(prev)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
// Don't call Insert(dev) directly, because successful Insert modifies dev.DeviceID,
|
||||
// but we don't do the same for other databases. That's why we create a copy.
|
||||
devCopy := *dev
|
||||
devCopy.CreatedAt = created
|
||||
_, err = dbSession.Table(tableName).Insert(devCopy)
|
||||
return err
|
||||
}
|
||||
|
||||
// Include all columns in the update, even when empty (it's what inserts for other databases do too).
|
||||
_, err = dbSession.Table(tableName).Where("device_id=?", dev.DeviceID).MustCols("client_ip", "user_agent", "created_at", "updated_at").Update(dev)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (s *AnonDBStore) CountDevices(ctx context.Context, from time.Time, to time.Time) (int64, error) {
|
||||
var count int64
|
||||
err := s.sqlStore.WithDbSession(ctx, func(dbSession *sqlstore.DBSession) error {
|
||||
|
||||
@@ -922,7 +922,39 @@ func (d *dashboardStore) FindDashboards(ctx context.Context, query *dashboards.F
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
if len(res) <= 1 {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// the search query above will return one row per dashboard tag, and dashboards
|
||||
// can have multiple tags. we only want to return one row per dashboard, so dedup
|
||||
// the results by id.
|
||||
// note: we must preserve the order of the results as we dedup, as the query can be sorted
|
||||
seen := make(map[int64]int)
|
||||
uniqueRes := make([]dashboards.DashboardSearchProjection, 0, len(res))
|
||||
|
||||
for _, item := range res {
|
||||
if idx, exists := seen[item.ID]; exists {
|
||||
if item.Term != "" {
|
||||
if uniqueRes[idx].Tags == nil {
|
||||
uniqueRes[idx].Tags = make([]string, 0)
|
||||
}
|
||||
uniqueRes[idx].Tags = append(uniqueRes[idx].Tags, item.Term)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if item.Tags == nil {
|
||||
item.Tags = make([]string, 0)
|
||||
}
|
||||
if item.Term != "" {
|
||||
item.Tags = append(item.Tags, item.Term)
|
||||
}
|
||||
seen[item.ID] = len(uniqueRes)
|
||||
uniqueRes = append(uniqueRes, item)
|
||||
}
|
||||
|
||||
return uniqueRes, nil
|
||||
}
|
||||
|
||||
func (d *dashboardStore) GetDashboardTags(ctx context.Context, query *dashboards.GetDashboardTagsQuery) ([]*dashboards.DashboardTagCloudItem, error) {
|
||||
|
||||
@@ -234,10 +234,6 @@ func TestIntegrationDashboardInheritedFolderRBAC(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
if db.IsTestDBSpanner() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
|
||||
// the maximux nested folder hierarchy starting from parent down to subfolders
|
||||
nestedFolders := make([]*folder.Folder, 0, folder.MaxNestedFolderDepth+1)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user